All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.snmp4j.mp.MPv3 Maven / Gradle / Ivy

There is a newer version: 3.8.2
Show newest version
/*_############################################################################
  _## 
  _##  SNMP4J 2 - MPv3.java  
  _## 
  _##  Copyright (C) 2003-2016  Frank Fock and Jochen Katz (SNMP4J.org)
  _##  
  _##  Licensed under the Apache License, Version 2.0 (the "License");
  _##  you may not use this file except in compliance with the License.
  _##  You may obtain a copy of the License at
  _##  
  _##      http://www.apache.org/licenses/LICENSE-2.0
  _##  
  _##  Unless required by applicable law or agreed to in writing, software
  _##  distributed under the License is distributed on an "AS IS" BASIS,
  _##  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  _##  See the License for the specific language governing permissions and
  _##  limitations under the License.
  _##  
  _##########################################################################*/
package org.snmp4j.mp;

import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.util.*;

import org.snmp4j.*;
import org.snmp4j.asn1.*;
import org.snmp4j.event.*;
import org.snmp4j.log.*;
import org.snmp4j.security.*;
import org.snmp4j.smi.*;
import org.snmp4j.util.PDUFactory;

/**
 * The {@code MPv3} is the message processing model for SNMPv3.
 *
 * @author Frank Fock
 * @version 1.9.2
 */
public class MPv3
    implements MessageProcessingModel, EngineIdCacheSize {

  public static final int ID = MessageProcessingModel.MPv3;
  public static final int MPv3_REPORTABLE_FLAG = 4;
  public static final int MAX_MESSAGE_ID = 2147483647;

  private static final int INT_LOW_16BIT_MASK = 0x0000FFFF;

  /**
   * Local engine ID constant for context engineID discovery
   * as defined by RFC 5343.
   */
  public static final OctetString LOCAL_ENGINE_ID =
      OctetString.fromHexString("80:00:00:00:06");

  public static final int MAXLEN_ENGINE_ID = 32;
  public static final int MINLEN_ENGINE_ID = 5;

  private static final int MAX_HEADER_PAYLOAD_LENGTH =
      // length of msgFlags
      new OctetString("\0").getBERLength() +
      // length of msgID, msgMaxSize, securityModel
      3 * new Integer32(Integer.MAX_VALUE).getBERLength();

  private static final int MAX_HEADER_LENGTH =
      MAX_HEADER_PAYLOAD_LENGTH +
      BER.getBERLengthOfLength(MAX_HEADER_PAYLOAD_LENGTH) + 1;

  private SecurityProtocols securityProtocols;

  private static final LogAdapter logger = LogFactory.getLogger(MPv3.class);
  private SecurityModels securityModels;

  private Cache cache;
  private Map engineIDs;
  private int maxEngineIdCacheSize = SNMP4JSettings.getMaxEngineIdCacheSize();
  private byte[] localEngineID;

  private int currentMsgID = new Random().nextInt(MAX_MESSAGE_ID);

  private CounterSupport counterSupport;
  protected EngineIdCacheFactory engineIdCacheFactory = new LimitedCapacityEngineIdCacheFactory();

  transient List snmpEngineListeners;

  protected PDUFactory incomingPDUFactory = new PDUFactory() {
    @Override
    public PDU createPDU(Target target) {
      return new ScopedPDU();
    }
    @Override
    public PDU createPDU(MessageProcessingModel messageProcessingModel) {
      return new ScopedPDU();
    }
  };

  /**
   * Creates a MPv3 with a default local engine ID.
   */
  public MPv3() {
    this(createLocalEngineID(), null);
  }

  /**
   * Creates a MPv3 with a supplied local engine ID.
   * @param localEngineID
   *    the local engine ID. Its length must be greater or equal than 5 and less or equal than 32.
   */
  public MPv3(byte[] localEngineID) {
    this(localEngineID, null);
    setLocalEngineID(localEngineID);
  }

  /**
   * Creates a MPv3 with a supplied local engine ID and {@link PDUFactory}
   * for incoming messages.
   * @param localEngineID
   *    the local engine ID. Its length must be greater or equal than 5 and less or equal than 32.
   * @param incomingPDUFactory
   *    a {@link PDUFactory}. If {@code null} the default factory will be
   *    used which creates {@link ScopedPDU} instances.
   * @since 1.9.1
   */
  public MPv3(byte[] localEngineID, PDUFactory incomingPDUFactory) {
    this(localEngineID, incomingPDUFactory, SecurityProtocols.getInstance(),
        SecurityModels.getInstance(), CounterSupport.getInstance());
  }

  /**
   * This is a convenience constructor which can be used to create a MPv3 which
   * is bound to a specific USM instance. A dedicated USM instance per
   * MPv3 is necessary if multiple {@link Snmp} instances are used within a VM.
   * @param usm
   *    an USM instance.
   * @since 1.10
   */
  public MPv3(USM usm) {
    this(usm.getLocalEngineID().getValue(), null,
        SecurityProtocols.getInstance(),
        SecurityModels.getCollection(new SecurityModel[]{usm}),
        CounterSupport.getInstance());
  }

  /**
   * Creates a fully qualified MPv3 instance with custom security protocols
   * and models as well as a custom counter support.
   * The current message ID is set using the USM engine boots counter (if available)
   * according to the RFC3412 §6.2.
   * @param localEngineID
   *    the local engine ID. Its length must be greater or equal than 5 and less or equal than 32.
   * @param incomingPDUFactory
   *    a {@link PDUFactory}. If {@code null} the default factory will be
   *    used which creates {@link ScopedPDU} instances.
   * @param secProtocols
   *    the SecurityProtocols instance to use when looking up a security
   *    protocol. To get a default instance, use
   *    {@link SecurityProtocols#getInstance()}.
   * @param secModels
   *    the SecurityModels instance to use when looking up a security model.
   *    If you use more than one USM instance, you need to create a
   *    SecurityProtocols instance (container) for each such USM instance (and
   *    MPv3 combination). To get a default instance, use
   *    {@link SecurityProtocols#getInstance()}.
   * @param counterSupport
   *    The CounterSupport instance to be used to count events created by this
   *    MPv3 instance. To get a default instance, use
   *    {@link CounterSupport#getInstance()}.
   * @since 1.10
   */
  public MPv3(byte[] localEngineID, PDUFactory incomingPDUFactory,
      SecurityProtocols secProtocols,
      SecurityModels secModels,
      CounterSupport counterSupport) {
    if (incomingPDUFactory != null) {
      this.incomingPDUFactory = incomingPDUFactory;
    }
    engineIDs = engineIdCacheFactory.createEngineIdMap(this);
    cache = new Cache();
    if (secProtocols == null) {
      throw new NullPointerException();
    }
    securityProtocols = secProtocols;
    if (secModels == null) {
      throw new NullPointerException();
    }
    securityModels = secModels;
    if (counterSupport == null) {
      throw new NullPointerException();
    }
    this.counterSupport = counterSupport;
    setLocalEngineID(localEngineID);
    SecurityModel usm = secModels.getSecurityModel(new Integer32(USM.SECURITY_MODEL_USM));
    if (usm instanceof USM) {
      setCurrentMsgID(randomMsgID(((USM)usm).getEngineBoots()));
    }
  }

  /**
   * Returns the enging ID factory that was used to create the current engine ID cache.
   * @return
   *    a {@link org.snmp4j.mp.MPv3.EngineIdCacheFactory} implementation.
   * @since 2.3.4
   */
  public EngineIdCacheFactory getEngineIdCacheFactory() {
    return engineIdCacheFactory;
  }

  /**
   * Sets the engine ID cache factory and resets (clears) the current cache. The maximum size of the
   * cache is determined using {@link #getMaxEngineIdCacheSize()} as this implements the {@link EngineIdCacheSize}
   * interface. By default the maximum cache size {@link SNMP4JSettings#getMaxEngineIdCacheSize()} is used.
   * @param engineIdCacheFactory
   *    a {@link org.snmp4j.mp.MPv3.EngineIdCacheFactory} implementation that is used to create a new cache.
   * @since 2.3.4
   */
  public void setEngineIdCacheFactory(EngineIdCacheFactory engineIdCacheFactory) {
    engineIDs = engineIdCacheFactory.createEngineIdMap(this);
    this.engineIdCacheFactory = engineIdCacheFactory;
  }

  @Override
  public int getMaxEngineIdCacheSize() {
    return maxEngineIdCacheSize;
  }

  /**
   * Sets the upper limit for the engine ID cache. Modifying this value will not immediately
   * take effect on the cache size.
   * @param maxEngineIdCacheSize
   *    the maximum number of engine IDs hold in the internal cache. If more than
   *    those engine IDs are used by the MPv3, the eldest engine ID is removed
   *    from the cache. Eldest means the eldest initial use.
   *    A different cache can be implemented by using a custom
   *    {@link EngineIdCacheFactory} and setting it after calling
   *    this constructor.
   */
  public void setMaxEngineIdCacheSize(int maxEngineIdCacheSize) {
    this.maxEngineIdCacheSize = maxEngineIdCacheSize;
  }

  /**
   * Creates a local engine ID based on the local IP address and additional four random bytes.
   * WARNING: Do not use this engine ID generator for a command responder (agent) if you DO NOT
   * persistently save the one time generated engine ID for subsequent use when the agent is
   * restarted.
   *
   * @return
   *    a new local engine ID with a random part to avoid engine ID clashes for multiple command
   *    generators on the same system.
   */
  public static byte[] createLocalEngineID() {
    int enterpriseID = SNMP4JSettings.getEnterpriseID();
    byte[] engineID = new byte[5];
    engineID[0] = (byte)(0x80 | ((enterpriseID >> 24) & 0xFF));
    engineID[1] = (byte)((enterpriseID >> 16) & 0xFF);
    engineID[2] = (byte)((enterpriseID >> 8) & 0xFF);
    engineID[3] = (byte)(enterpriseID & 0xFF);
    engineID[4] = 2;
    OctetString os = new OctetString();
    try {
      byte[] b = InetAddress.getLocalHost().getAddress();
      if (b.length == 4) {
        engineID[4] = 1;
      }
      os.setValue(b);
    }
    catch (UnknownHostException ex) {
      logger.debug("Local host cannot be determined for creation of local engine ID");
      engineID[4] = 4;
      os.setValue("SNMP4J".getBytes());
    }
    OctetString ownEngineID = new OctetString(engineID);
    Random random = new Random(System.nanoTime());
    byte[] fourBytes = new byte[4];
    random.nextBytes(fourBytes);
    ownEngineID.append(os);
    ownEngineID.append(fourBytes);
    return ownEngineID.getValue();
  }

  /**
   * Creates a local engine ID based on the ID string supplied
   * @param id
   *    an ID string.
   * @return
   *    a new local engine ID.
   */
  public static byte[] createLocalEngineID(OctetString id) {
    int enterpriseID =SNMP4JSettings.getEnterpriseID();
    byte[] engineID = new byte[5];
    engineID[0] = (byte)(0x80 | ((enterpriseID >> 24) & 0xFF));
    engineID[1] = (byte)((enterpriseID >> 16) & 0xFF);
    engineID[2] = (byte)((enterpriseID >> 8) & 0xFF);
    engineID[3] = (byte)(enterpriseID & 0xFF);
    engineID[4] = 4;
    OctetString ownEngineID = new OctetString(engineID);
    ownEngineID.append(id);
    return ownEngineID.getValue();
  }

  /**
   * Sets the local engine ID. This value must not be changed after message
   * processing has been started.
   * Note: When setting the local engine ID, the engine boots counter should
   * be known at the same time. Thus, please also call
   * 
   *   setCurrentMsgID(randomMsgID(engineBoots));
   * 
* before starting the message processing. * @param engineID * the local engine ID. Its length must be greater or equal than 5 and less or equal than 32. */ public void setLocalEngineID(byte[] engineID) { if ((engineID == null) || (engineID.length < MINLEN_ENGINE_ID) || (engineID.length > MAXLEN_ENGINE_ID)) { throw new IllegalArgumentException("Illegal (local) engine ID"); } this.localEngineID = engineID; } /** * Creates a random message ID according to the method proposed by RFC3412: * "Values for msgID SHOULD be generated in a manner that avoids re-use * of any outstanding values. Doing so provides protection against some * replay attacks. One possible implementation strategy would be to use * the low-order bits of snmpEngineBoots [RFC3411] as the high-order * portion of the msgID value and a monotonically increasing integer for * the low-order portion of msgID." * * @param engineBoots * the number of boot operations already occurred for this SNMP entity. * @return * the new random message ID. */ public static int randomMsgID(int engineBoots) { return (new Random().nextInt(MAX_MESSAGE_ID) & INT_LOW_16BIT_MASK) | ((engineBoots & INT_LOW_16BIT_MASK) << 16); } /** * Gets a copy of the local engine ID. * @return * a byte array containing the local engine ID. */ public byte[] getLocalEngineID() { byte[] retval = new byte[localEngineID.length]; System.arraycopy(localEngineID, 0, retval, 0, localEngineID.length); return retval; } /** * Creates and initializes the default security protocols. * @see SecurityProtocols#addDefaultProtocols() */ public void initDefaults() { securityProtocols.addDefaultProtocols(); } /** * Gets an authentication protocol for the supplied ID. * @param id * an authentication protocol OID. * @return * an {@link AuthenticationProtocol} instance if the supplied ID * is supported, otherwise {@code null} is returned. */ public AuthenticationProtocol getAuthProtocol(OID id) { return securityProtocols.getAuthenticationProtocol(id); } /** * Gets an privacy protocol for the supplied ID. * @param id * an privacy protocol OID. * @return * an {@link PrivacyProtocol} instance if the supplied ID * is supported, otherwise {@code null} is returned. */ public PrivacyProtocol getPrivProtocol(OID id) { return securityProtocols.getPrivacyProtocol(id); } /** * Gets the security model for the supplied ID. * @param id * a security model ID. * @return * a {@link SecurityModel} instance if the supplied ID * is supported, otherwise {@code null} is returned. */ public SecurityModel getSecurityModel(int id) { return securityModels.getSecurityModel(new Integer32(id)); } public int getID() { return ID; } public boolean isProtocolVersionSupported(int version) { return (version == SnmpConstants.version3); } /** * Adds an engine ID (other than the local engine ID) to the internal storage. * @param address * the {@link Address} of the remote SNMP engine. * @param engineID * the engine ID of the remote SNMP engine. * @return * {@code true} if the engine ID has been added, false * otherwise (if the supplied {@code engineID} equals the local one). */ public boolean addEngineID(Address address, OctetString engineID) { if (!Arrays.equals(this.localEngineID, engineID.getValue())) { try { OctetString previousEngineID = addEngineIdToCache(address, engineID); if ((snmpEngineListeners != null) && ((previousEngineID == null) || (!previousEngineID.equals(engineID)))) { fireEngineChanged(new SnmpEngineEvent(this, SnmpEngineEvent.ADDED_ENGINE_ID, engineID, address)); } } catch (IllegalArgumentException iaex) { fireEngineChanged(new SnmpEngineEvent(this, SnmpEngineEvent.IGNORED_ENGINE_ID, engineID, address)); return false; } return true; } return false; } /** * Put the engine ID for the given address into the internal cache. If the cache * reached its limit, * @param address * the address of the engine ID * @param engineID * the engine ID to cache. * @return * the previous engine ID or {@code null} if there was no engine ID * cached for the given address. * @throws IllegalArgumentException when the local maximum cache size is exceeded. * @since 2.3.4 */ protected OctetString addEngineIdToCache(Address address, OctetString engineID) { if ((maxEngineIdCacheSize > 0) && (engineIDs.size() >= maxEngineIdCacheSize)) { if (engineIDs.containsKey(address)) { return engineIDs.put(address, engineID); } String msg = "MPv3: Failed to add engineID '"+engineID.toHexString()+"' for address '"+address+ "' to local cache because its size limit of "+maxEngineIdCacheSize+"has been reached"; logger.warn(msg); throw new IllegalArgumentException(msg); } else { return engineIDs.put(address, engineID); } } /** * Gets the engine ID associated with the supplied address from the local * storage and fires the corresponding {@link SnmpEngineEvent}. * @param address * the {@link Address} of the remote SNMP engine. * @return * the engine ID of the remote SNMP engine or {@code null} if there * is no entry for {@code address} in the local storage. */ public OctetString getEngineID(Address address) { return engineIDs.get(address); } /** * Removes an engine ID association from the local storage and fires the * corresponding {@link SnmpEngineEvent}. * @param address * the {@link Address} of the remote SNMP engine for whose engine ID * is to be removed. * @return * the removed engine ID of the remote SNMP engine or {@code null} if * there is no entry for {@code address} in the local storage. */ public OctetString removeEngineID(Address address) { OctetString engineID = engineIDs.remove(address); if ((engineID != null) && (snmpEngineListeners != null)) { fireEngineChanged(new SnmpEngineEvent(this, SnmpEngineEvent.REMOVED_ENGINE_ID, engineID, address)); } return engineID; } /** * The {@code CacheEntry} class holds state reference information * for the MPv3 message processing model for a single message. * @author Frank Fock * @version 1.0 */ protected static class CacheEntry extends StateReference { int msgID; long transactionID; byte[] secEngineID; SecurityModel secModel; byte[] secName; int secLevel; byte[] contextEngineID; byte[] contextName; SecurityStateReference secStateReference; int errorCode; public CacheEntry(int msgID, long reqID, byte[] secEngineID, SecurityModel secModel, byte[] secName, int secLevel, byte[] contextEngineID, byte[] contextName, SecurityStateReference secStateReference, int errorCode) { this.msgID = msgID; this.transactionID = reqID; this.secEngineID = secEngineID; this.secModel = secModel; this.secName = secName; this.secLevel = secLevel; this.contextEngineID = contextEngineID; this.contextName = contextName; this.secStateReference = secStateReference; this.errorCode = errorCode; } } /** * The {@code Cache} stores state reference information for the MPv3. * @author Frank Fock * @version 1.0 */ protected static class Cache { private Map entries = new WeakHashMap(25); /** * Adds a StateReference to the cache. * The PduHandle of the supplied entry will be set to * null when the new entry is already part of the cache, because the * cache uses a WeakHashMap internally which uses the * PduHandle as key. If the new entry equals an existing entry * except of the message ID then the new message ID will be added to the * existing entry. * @param entry * the state reference to add. * @return * {@link SnmpConstants#SNMP_MP_DOUBLED_MESSAGE} if the entry already * exists and {@link SnmpConstants#SNMP_MP_OK} on success. */ public synchronized int addEntry(StateReference entry) { if (logger.isDebugEnabled()) { logger.debug("Adding cache entry: "+entry); } StateReference existing = entries.get(entry.getPduHandle()); if (existing != null) { // reassign handle for comparison: existing.setPduHandle(entry.getPduHandle()); if (existing.equals(entry)) { if (logger.isDebugEnabled()) { logger.debug("Doubled message: "+entry); } // clear it again to remove strong self-reference existing.setPduHandle(null); return SnmpConstants.SNMP_MP_DOUBLED_MESSAGE; } else if (existing.equalsExceptMsgID(entry)) { if (logger.isDebugEnabled()) { logger.debug("Adding previous message IDs "+existing.getMessageIDs()+" to new entry "+entry); } entry.addMessageIDs(existing.getMessageIDs()); } else if (logger.isDebugEnabled()) { logger.debug("New entry does not match existing, although request ID is the same "+entry+" != "+existing); } // clear it again to remove strong self-reference existing.setPduHandle(null); } // add it PduHandle key = entry.getPduHandle(); // because we are using a weak hash map for the cache, we need to null out // our key from the entry. entry.setPduHandle(null); entries.put(key, entry); return SnmpConstants.SNMP_MP_OK; } /** * Delete the cache entry with the supplied {@link PduHandle}. * @param pduHandle * a pduHandle. * @return * {@code true} if an entry has been deleted, {@code false} * otherwise. */ public synchronized boolean deleteEntry(PduHandle pduHandle) { StateReference e = entries.remove(pduHandle); return (e != null); } /** * Pop the cache entry with the supplied ID from the cache. * @param msgID * a message ID. * @return * a {@link CacheEntry} instance with the given message ID or * {@code null} if such an entry cannot be found. If a cache entry * is returned, the same is removed from the cache. */ public synchronized StateReference popEntry(int msgID) { for (Iterator it = entries.keySet().iterator(); it.hasNext(); ) { PduHandle key = it.next(); StateReference e = entries.get(key); if ((e != null) && (e.isMatchingMessageID(msgID))) { it.remove(); e.setPduHandle(key); if (logger.isDebugEnabled()) { logger.debug("Removed cache entry: "+e); } return e; } } return null; } } /** * The {@code HeaderData} represents the message header information * of SNMPv3 message. * @author Frank Fock * @version 1.0 */ public static class HeaderData implements BERSerializable { public static final byte FLAG_AUTH = 0x01; public static final byte FLAG_PRIV = 0x02; Integer32 msgID = new Integer32(0); Integer32 msgMaxSize = new Integer32(Integer.MAX_VALUE); OctetString msgFlags = new OctetString(new byte[1]); Integer32 securityModel = new Integer32(SecurityModel.SECURITY_MODEL_ANY); public void setMsgID(int msgID) { this.msgID.setValue(msgID); } public int getMsgID() { return msgID.getValue(); } public void setMsgMaxSize(int msgMaxSize) { this.msgMaxSize.setValue(msgMaxSize); } public int getMsgMaxSize() { return msgMaxSize.getValue(); } public void setMsgFlags(int flags) { this.msgFlags.getValue()[0] = (byte) flags; } public int getMsgFlags() { return msgFlags.getValue()[0] & 0xFF; } public void setSecurityModel(int model) { securityModel.setValue(model); } public int getSecurityModel() { return securityModel.getValue(); } public int getBERPayloadLength() { int length = msgID.getBERLength(); length += msgMaxSize.getBERLength(); length += msgFlags.getBERLength(); length += securityModel.getBERLength(); return length; } public int getBERLength() { int length = getBERPayloadLength(); length += BER.getBERLengthOfLength(length) + 1; return length; } public void decodeBER(BERInputStream message) throws IOException { BER.MutableByte type = new BER.MutableByte(); int length = BER.decodeHeader(message, type); if (type.getValue() != BER.SEQUENCE) { throw new IOException("Unexpected sequence header type: " + type.getValue()); } msgID.decodeBER(message); msgMaxSize.decodeBER(message); if (msgMaxSize.getValue() < 484) { throw new IOException("Invalid msgMaxSize: " + msgMaxSize); } msgFlags.decodeBER(message); if (msgFlags.length() != 1) { throw new IOException("Message flags length != 1: " + msgFlags.length()); } securityModel.decodeBER(message); if (logger.isDebugEnabled()) { logger.debug("SNMPv3 header decoded: msgId=" + msgID + ", msgMaxSize=" + msgMaxSize + ", msgFlags=" + msgFlags.toHexString() + ", secModel=" + securityModel); } BER.checkSequenceLength(length, this); } public void encodeBER(OutputStream outputStream) throws IOException { BER.encodeHeader(outputStream, BER.SEQUENCE, getBERPayloadLength()); msgID.encodeBER(outputStream); msgMaxSize.encodeBER(outputStream); msgFlags.encodeBER(outputStream); securityModel.encodeBER(outputStream); } } /** * Gets unique message ID. * @return * a message ID greater or equal to one and less or equal {@link #MAX_MESSAGE_ID}. */ public synchronized int getNextMessageID() { if (currentMsgID >= MAX_MESSAGE_ID) { currentMsgID = 1; } return currentMsgID++; } /** * Gets the security protocols supported by this {@link MPv3}. * @return * return a {@link SecurityProtocols}. */ public SecurityProtocols getSecurityProtocols() { return securityProtocols; } /** * Sets the security protocols for this {@link MPv3}. * @param securityProtocols SecurityProtocols */ public void setSecurityProtocols(SecurityProtocols securityProtocols) { this.securityProtocols = securityProtocols; } public void releaseStateReference(PduHandle pduHandle) { cache.deleteEntry(pduHandle); } public int prepareOutgoingMessage(Address transportAddress, int maxMessageSize, int messageProcessingModel, int securityModel, byte[] securityName, int securityLevel, PDU pdu, boolean expectResponse, PduHandle sendPduHandle, Address destTransportAddress, BEROutputStream outgoingMessage, TransportStateReference tmStateReference) throws IOException { if (!(pdu instanceof ScopedPDU)) { throw new IllegalArgumentException( "MPv3 only accepts ScopedPDU instances as pdu parameter"); } ScopedPDU scopedPDU = (ScopedPDU) pdu; SecurityModel secModel = securityModels.getSecurityModel(new Integer32(securityModel)); if (secModel == null) { return SnmpConstants.SNMP_MP_UNSUPPORTED_SECURITY_MODEL; } // lookup engine ID byte[] secEngineID; if (secModel.hasAuthoritativeEngineID()) { OctetString securityEngineID = engineIDs.get(transportAddress); if (securityEngineID != null) { secEngineID = securityEngineID.getValue(); if (scopedPDU.getContextEngineID().length() == 0) { switch (pdu.getType()) { case PDU.NOTIFICATION: case PDU.INFORM: { OctetString localEngineID = new OctetString(getLocalEngineID()); if (logger.isDebugEnabled()) { logger.debug("Context engine ID of scoped PDU is empty! Setting it to local engine ID: "+ localEngineID.toHexString()); } scopedPDU.setContextEngineID(localEngineID); } default: if (logger.isDebugEnabled()) { logger.debug("Context engine ID of scoped PDU is empty! Setting it to authoritative engine ID: "+ securityEngineID.toHexString()); } scopedPDU.setContextEngineID(new OctetString(secEngineID)); } } } else { secEngineID = new byte[0]; } } else { secEngineID = new byte[0]; } // determine request type if (pdu.isConfirmedPdu()) { if (secEngineID.length == 0) { if (secModel.supportsEngineIdDiscovery()) { securityLevel = SecurityLevel.NOAUTH_NOPRIV; // do not send any management information scopedPDU = (ScopedPDU) scopedPDU.clone(); scopedPDU.clear(); } else if ((scopedPDU.getContextEngineID() == null) || (scopedPDU.getContextEngineID().length() == 0)) { logger.warn("ScopedPDU with empty context engine ID"); } else if (!LOCAL_ENGINE_ID.equals(scopedPDU.getContextEngineID()) && getEngineID(transportAddress) == null) { // Learn context engine ID addEngineID(transportAddress, scopedPDU.getContextEngineID()); } } } else { if (scopedPDU.getContextEngineID().length() == 0) { if (logger.isDebugEnabled()) { logger.debug("Context engine ID of unconfirmed scoped PDU is empty! "+ "Setting it to local engine ID"); } scopedPDU.setContextEngineID(new OctetString(localEngineID)); } } // get length of scoped PDU int scopedPDULength = scopedPDU.getBERLength(); BEROutputStream scopedPdu = new BEROutputStream(ByteBuffer.allocate(scopedPDULength)); scopedPDU.encodeBER(scopedPdu); HeaderData headerData = new HeaderData(); int flags = 0; switch (securityLevel) { case SecurityLevel.NOAUTH_NOPRIV: flags = 0; break; case SecurityLevel.AUTH_NOPRIV: flags = 1; break; case SecurityLevel.AUTH_PRIV: flags = 3; break; } if (scopedPDU.isConfirmedPdu()) { flags |= MPv3_REPORTABLE_FLAG; } else { secEngineID = localEngineID; } int msgID = getNextMessageID(); headerData.setMsgFlags(flags); headerData.setMsgID(msgID); headerData.setMsgMaxSize(maxMessageSize); headerData.setSecurityModel(securityModel); ByteBuffer globalDataBuffer = ByteBuffer.allocate(headerData.getBERLength()); BEROutputStream globalDataOutputStream = new BEROutputStream(globalDataBuffer); headerData.encodeBER(globalDataOutputStream); BERInputStream scopedPDUInput = new BERInputStream(scopedPdu.rewind()); // output data SecurityParameters securityParameters = secModel.newSecurityParametersInstance(); int status = secModel.generateRequestMessage(messageProcessingModel, globalDataBuffer.array(), maxMessageSize, securityModel, secEngineID, securityName, securityLevel, scopedPDUInput, securityParameters, outgoingMessage, tmStateReference); if (status == SnmpConstants.SNMPv3_USM_OK) { if (expectResponse) { cache.addEntry(new StateReference(msgID, flags, maxMessageSize, sendPduHandle, transportAddress, null, secEngineID, secModel, securityName, securityLevel, scopedPDU.getContextEngineID(). getValue(), scopedPDU.getContextName().getValue(), null, status)); } } return status; } public int prepareResponseMessage(int messageProcessingModel, int maxMessageSize, int securityModel, byte[] securityName, int securityLevel, PDU pdu, int maxSizeResponseScopedPDU, StateReference stateReference, StatusInformation statusInformation, BEROutputStream outgoingMessage) throws IOException { /** Leave entry in cache or remove it? RFC3414 §3.1.a.1 says discard it*/ StateReference cacheEntry = cache.popEntry(stateReference.getMsgID().getID()); if (cacheEntry == null) { return SnmpConstants.SNMP_MP_UNKNOWN_MSGID; } // get length of scoped PDU // get length of scoped PDU int scopedPDULength = pdu.getBERLength(); BEROutputStream scopedPDU; // check length if (scopedPDULength > maxSizeResponseScopedPDU) { PDU tooBigPDU = new ScopedPDU((ScopedPDU)pdu); tooBigPDU.clear(); tooBigPDU.setRequestID(pdu.getRequestID()); tooBigPDU.setErrorStatus(SnmpConstants.SNMP_ERROR_TOO_BIG); tooBigPDU.setErrorIndex(0); scopedPDULength = tooBigPDU.getBERLength(); scopedPDU = new BEROutputStream(ByteBuffer.allocate(scopedPDULength)); tooBigPDU.encodeBER(scopedPDU); } else { scopedPDU = new BEROutputStream(ByteBuffer.allocate(scopedPDULength)); pdu.encodeBER(scopedPDU); } HeaderData headerData = new HeaderData(); int flags = 0; switch (securityLevel) { case SecurityLevel.NOAUTH_NOPRIV: flags = 0; break; case SecurityLevel.AUTH_NOPRIV: flags = 1; break; case SecurityLevel.AUTH_PRIV: flags = 3; break; } // response message is not reportable headerData.setMsgFlags(flags); headerData.setMsgID(stateReference.getMsgID().getID()); headerData.setMsgMaxSize(maxMessageSize); headerData.setSecurityModel(securityModel); ByteBuffer globalDataBuffer = ByteBuffer.allocate(headerData.getBERLength()); BEROutputStream globalDataOutputStream = new BEROutputStream(globalDataBuffer); headerData.encodeBER(globalDataOutputStream); OctetString securityEngineID; switch (pdu.getType()) { case PDU.RESPONSE: case PDU.TRAP: case PDU.REPORT: case PDU.V1TRAP: securityEngineID = new OctetString(localEngineID); break; default: securityEngineID = new OctetString(cacheEntry.getSecurityEngineID()); } BERInputStream scopedPDUInput = new BERInputStream(scopedPDU.rewind()); SecurityModel secModel = securityModels.getSecurityModel(new Integer32(securityModel)); // output data SecurityParameters securityParameters = secModel.newSecurityParametersInstance(); return secModel.generateResponseMessage(getID(), globalDataBuffer.array(), maxMessageSize, securityModel, securityEngineID.getValue(), securityName, securityLevel, scopedPDUInput, cacheEntry.getSecurityStateReference(), securityParameters, outgoingMessage); } /** * Sends a report message. * @param messageDispatcher * Send the message on behalf the supplied MessageDispatcher instance. * @param pdu ScopedPDU * If {@code null}, then contextEngineID, contextName, and requestID * of the report generated will be zero length and zero respective. * Otherwise these values are extracted from the PDU. * @param securityLevel * The security level to use when sending this report. * @param securityModel * The security model to use when sending this report. * @param securityName * The security name to use when sending this report. * @param maxSizeResponseScopedPDU * the maximum size of of the report message (will be most likely ignored * because a report should always fit in 484 bytes). * @param stateReference * the state reference associated with the original message. * @param payload * the variable binding to include in the report message. * @return * an SNMP MPv3 error code or 0 if the report has been send successfully. */ public int sendReport(MessageDispatcher messageDispatcher, ScopedPDU pdu, int securityLevel, int securityModel, OctetString securityName, int maxSizeResponseScopedPDU, StateReference stateReference, VariableBinding payload) { ScopedPDU reportPDU = new ScopedPDU(); reportPDU.setType(PDU.REPORT); if (pdu != null) { reportPDU.setContextEngineID(pdu.getContextEngineID()); reportPDU.setContextName(pdu.getContextName()); reportPDU.setRequestID(pdu.getRequestID()); } else { // RFC 3412 §7.1.3d) reportPDU.setContextEngineID(new OctetString(getLocalEngineID())); } reportPDU.add(payload); StatusInformation statusInformation = new StatusInformation(); try { int status = messageDispatcher.returnResponsePdu(getID(), securityModel, securityName.getValue(), securityLevel, reportPDU, maxSizeResponseScopedPDU, stateReference, statusInformation); if (status != SnmpConstants.SNMP_ERROR_SUCCESS) { logger.warn("Error while sending report: " + status); return SnmpConstants.SNMP_MP_ERROR; } } catch (MessageException mex) { logger.error("Error while sending report: " + mex.getMessage()); return SnmpConstants.SNMP_MP_ERROR; } return SnmpConstants.SNMP_MP_OK; } public int prepareDataElements(MessageDispatcher messageDispatcher, Address transportAddress, BERInputStream wholeMsg, TransportStateReference tmStateReference, Integer32 messageProcessingModel, Integer32 securityModel, OctetString securityName, Integer32 securityLevel, MutablePDU pdu, PduHandle sendPduHandle, Integer32 maxSizeResponseScopedPDU, StatusInformation statusInformation, MutableStateReference mutableStateReference) { try { StateReference stateReference = new StateReference(); // check if there is transport mapping information if (mutableStateReference.getStateReference() != null) { stateReference.setTransportMapping( mutableStateReference.getStateReference().getTransportMapping()); } messageProcessingModel.setValue(MPv3); wholeMsg.mark(16); BER.MutableByte type = new BER.MutableByte(); int length = BER.decodeHeader(wholeMsg, type); if (type.getValue() != BER.SEQUENCE) { return SnmpConstants.SNMP_MP_PARSE_ERROR; } long lengthOfLength = wholeMsg.getPosition(); wholeMsg.reset(); wholeMsg.mark(length); if (wholeMsg.skip(lengthOfLength) != lengthOfLength) { return SnmpConstants.SNMP_MP_PARSE_ERROR; } Integer32 snmpVersion = new Integer32(); snmpVersion.decodeBER(wholeMsg); if (snmpVersion.getValue() != SnmpConstants.version3) { // internal error -> should not happen throw new RuntimeException( "Internal error unexpected SNMP version read"); } // decode SNMPv3 header HeaderData header = new HeaderData(); header.decodeBER(wholeMsg); securityModel.setValue(header.getSecurityModel()); stateReference.setMsgID(header.getMsgID()); stateReference.setMsgFlags(header.getMsgFlags()); stateReference.setAddress(transportAddress); mutableStateReference.setStateReference(stateReference); // the usm has to recalculate this value maxSizeResponseScopedPDU.setValue(header.msgMaxSize.getValue() - MAX_HEADER_LENGTH); ScopedPDU scopedPdu = (ScopedPDU) incomingPDUFactory.createPDU(this); pdu.setPdu(scopedPdu); SecurityModel secModel = securityModels.getSecurityModel(securityModel); if (secModel == null) { logger.error("RFC3412 §7.2.4 - Unsupported security model: " + securityModel); CounterEvent event = new CounterEvent(this, SnmpConstants.snmpUnknownSecurityModels); fireIncrementCounter(event); return SnmpConstants.SNMP_MP_UNSUPPORTED_SECURITY_MODEL; } // determine security level switch (header.getMsgFlags() & 0x03) { case 3: { securityLevel.setValue(SecurityLevel.AUTH_PRIV); break; } case 0: { securityLevel.setValue(SecurityLevel.NOAUTH_NOPRIV); break; } case 1: { securityLevel.setValue(SecurityLevel.AUTH_NOPRIV); break; } default: { securityLevel.setValue(SecurityLevel.NOAUTH_NOPRIV); logger.debug("RFC3412 §7.2.5 - Invalid message (illegal msgFlags)"); CounterEvent event = new CounterEvent(this, SnmpConstants.snmpInvalidMsgs); fireIncrementCounter(event); // do not send back report return SnmpConstants.SNMP_MP_INVALID_MESSAGE; } } statusInformation.setSecurityLevel(securityLevel); int secParametersPosition = (int)wholeMsg.getPosition(); // get security parameters SecurityParameters secParameters = secModel.newSecurityParametersInstance(); secParameters.decodeBER(wholeMsg); secParameters.setSecurityParametersPosition(secParametersPosition); // reportable flag boolean reportableFlag = ((header.getMsgFlags() & 0x04) > 0); OctetString securityEngineID = new OctetString(); // create a new security state reference SecurityStateReference secStateReference = secModel.newSecurityStateReference(); // create output stream for scoped PDU // may be optimized by an output stream that maps directly into the // original input wholeMsg.reset(); BEROutputStream scopedPDU = new BEROutputStream(); int status = secModel.processIncomingMsg(snmpVersion.getValue(), header.getMsgMaxSize() - MAX_HEADER_LENGTH, secParameters, secModel, securityLevel.getValue(), wholeMsg, tmStateReference, // output parameters securityEngineID, securityName, scopedPDU, maxSizeResponseScopedPDU, secStateReference, statusInformation); wholeMsg.close(); if (status == SnmpConstants.SNMPv3_USM_OK) { try { BERInputStream scopedPduStream = new BERInputStream(scopedPDU.rewind()); scopedPdu.decodeBER(scopedPduStream); sendPduHandle.setTransactionID(scopedPdu.getRequestID().getValue()); // add the engine ID to the local cache if it is a report or response, do not add traps. if ((securityEngineID.length() > 0) && scopedPdu.isResponsePdu()) { addEngineID(transportAddress, securityEngineID); } } catch (IOException iox) { logger.warn("ASN.1 parse error: "+iox.getMessage()); if (logger.isDebugEnabled()) { iox.printStackTrace(); } CounterEvent event = new CounterEvent(this, SnmpConstants. snmpInASNParseErrs); fireIncrementCounter(event); return SnmpConstants.SNMP_MP_PARSE_ERROR; } if (((scopedPdu.getContextEngineID() == null) || (scopedPdu.getContextEngineID().length() == 0)) && ((scopedPdu.getType() != PDU.RESPONSE) && (scopedPdu.getType() != PDU.REPORT))) { CounterEvent event = new CounterEvent(this, SnmpConstants. snmpUnknownPDUHandlers); fireIncrementCounter(event); VariableBinding errorIndication = new VariableBinding(event.getOid(), event.getCurrentValue()); statusInformation.setErrorIndication(errorIndication); status = SnmpConstants.SNMP_MP_UNKNOWN_PDU_HANDLERS; } } stateReference.setSecurityName(securityName.getValue()); stateReference.setSecurityEngineID(securityEngineID.getValue()); stateReference.setSecurityLevel(securityLevel.getValue()); stateReference.setSecurityModel(secModel); stateReference.setSecurityStateReference(secStateReference); stateReference.setPduHandle(sendPduHandle); if (status != SnmpConstants.SNMPv3_USM_OK) { if ((reportableFlag) && (statusInformation.getErrorIndication() != null)) { // RFC3412 §7.2.6.a - generate a report try { if (scopedPDU.getBuffer() != null) { BERInputStream scopedPduStream = new BERInputStream(scopedPDU.rewind()); scopedPdu.decodeBER(scopedPduStream); } else { // incoming message could not be decoded scopedPdu = null; } } catch (IOException iox) { logger.warn(iox); scopedPdu = null; } StateReference cacheEntry = new StateReference(header.getMsgID(), header.getMsgFlags(), maxSizeResponseScopedPDU.getValue(), sendPduHandle, transportAddress, null, securityEngineID.getValue(), secModel, securityName.getValue(), securityLevel.getValue(), (scopedPdu == null) ? new byte[0] : scopedPdu.getContextEngineID().getValue(), (scopedPdu == null) ? new byte[0] : scopedPdu.getContextName().getValue(), secStateReference, status); cache.addEntry(cacheEntry); int reportStatus = sendReport(messageDispatcher, scopedPdu, statusInformation.getSecurityLevel().getValue(), secModel.getID(), securityName, maxSizeResponseScopedPDU.getValue(), stateReference, statusInformation.getErrorIndication()); if (reportStatus != SnmpConstants.SNMP_MP_OK) { logger.warn("Sending report failed with error code: " + reportStatus); } } return status; } stateReference.setContextEngineID(scopedPdu.getContextEngineID().getValue()); stateReference.setContextName(scopedPdu.getContextName().getValue()); stateReference.setMaxSizeResponseScopedPDU(maxSizeResponseScopedPDU. getValue()); if ((scopedPdu.getType() == PDU.RESPONSE) || (scopedPdu.getType() == PDU.REPORT)) { StateReference cacheEntry = cache.popEntry(header.getMsgID()); if (cacheEntry != null) { if (logger.isDebugEnabled()) { logger.debug("RFC3412 §7.2.10 - Received PDU (msgID=" + header.getMsgID() + ") is a response or " + "an internal class message. PduHandle.transactionID = " + cacheEntry.getPduHandle().getTransactionID()); } sendPduHandle.copyFrom(cacheEntry.getPduHandle()); if (scopedPdu.getType() == PDU.REPORT) { statusInformation.setContextEngineID(scopedPdu.getContextEngineID().getValue()); statusInformation.setContextName(scopedPdu.getContextName().getValue()); statusInformation.setSecurityLevel(securityLevel); if (((cacheEntry.getSecurityEngineID().length != 0) && (!securityEngineID.equalsValue(cacheEntry.getSecurityEngineID()))) || (secModel.getID() != cacheEntry.getSecurityModel().getID()) || ((!securityName.equalsValue(cacheEntry.getSecurityName()) && (securityName.length() != 0)))) { if (logger.isDebugEnabled()) { logger.debug( "RFC 3412 §7.2.11 - Received report message does not match sent message. Cache entry is: "+ cacheEntry+", received secName="+securityName+",secModel="+secModel+ ",secEngineID="+securityEngineID); } //cache.deleteEntry(cacheEntry.getPduHandle()); mutableStateReference.setStateReference(null); return SnmpConstants.SNMP_MP_MATCH_ERROR; } if (!addEngineID(cacheEntry.getAddress(), securityEngineID)) { if (logger.isWarnEnabled()) { logger.warn("Engine ID '"+securityEngineID+ "' could not be added to engine ID cache for "+ "target address '"+cacheEntry.getAddress()+ "' because engine ID matches local engine ID or cache size limit is reached"); } } //cache.deleteEntry(cacheEntry.getPduHandle()); mutableStateReference.setStateReference(null); logger.debug("MPv3 finished"); return SnmpConstants.SNMP_MP_OK; } if (scopedPdu.getType() == PDU.RESPONSE) { if (((!securityEngineID.equalsValue(cacheEntry.getSecurityEngineID())) && (cacheEntry.getSecurityEngineID().length != 0)) || (secModel.getID() != cacheEntry.getSecurityModel().getID()) || (!securityName.equalsValue(cacheEntry.getSecurityName())) || (securityLevel.getValue() != cacheEntry.getSecurityLevel()) || ((!scopedPdu.getContextEngineID().equalsValue(cacheEntry.getContextEngineID())) && (cacheEntry.getContextEngineID().length != 0)) || ((!scopedPdu.getContextName().equalsValue(cacheEntry.getContextName()) && (cacheEntry.getContextName().length != 0)))) { logger.debug( "RFC 3412 §7.2.12.b - Received response message does not match sent message"); //cache.deleteEntry(cacheEntry.getPduHandle()); mutableStateReference.setStateReference(null); return SnmpConstants.SNMP_MP_MATCH_ERROR; } //cache.deleteEntry(cacheEntry.getPduHandle()); mutableStateReference.setStateReference(null); logger.debug("MPv3 finished"); return SnmpConstants.SNMP_MP_OK; } } else { if (logger.isDebugEnabled()) { logger.debug("RFC3412 §7.2.10 - Received PDU (msgID=" + header.getMsgID() + ") is a response or " + "internal class message, but cached " + "information for the msgID could not be found"); } return SnmpConstants.SNMP_MP_UNKNOWN_MSGID; } } else { logger.debug("RFC3412 §7.2.10 - Received PDU is NOT a response or " + "internal class message -> unchanged PduHandle = "+ sendPduHandle); } switch (scopedPdu.getType()) { case PDU.GET: case PDU.GETBULK: case PDU.GETNEXT: case PDU.INFORM: case PDU.SET: { if (securityEngineID.length() == 0) { logger.debug("Received confirmed message with 0 length security engine ID"); } else if (!securityEngineID.equalsValue(localEngineID)) { if (logger.isDebugEnabled()) { logger.debug("RFC 3412 §7.2.13.a - Security engine ID " + securityEngineID.toHexString() + " does not match local engine ID " + new OctetString(localEngineID).toHexString()); } mutableStateReference.setStateReference(null); return SnmpConstants.SNMP_MP_INVALID_ENGINEID; } int cacheStatus = cache.addEntry(stateReference); if (cacheStatus == SnmpConstants.SNMP_MP_DOUBLED_MESSAGE) { mutableStateReference.setStateReference(null); } return SnmpConstants.SNMP_MP_OK; } case PDU.TRAP: case PDU.V1TRAP: { mutableStateReference.setStateReference(null); return SnmpConstants.SNMP_MP_OK; } } // this line should not be reached return SnmpConstants.SNMP_MP_ERROR; } catch (IOException iox) { logger.warn("MPv3 parse error: " + iox.getMessage()); if (logger.isDebugEnabled()) { iox.printStackTrace(); } return SnmpConstants.SNMP_MP_PARSE_ERROR; } } /** * Sets the security models supported by this MPv3. * @param securityModels * a {@link SecurityModels} instance. */ public void setSecurityModels(SecurityModels securityModels) { this.securityModels = securityModels; } /** * Gets the security models supported by this MPv3. * @return * a {@link SecurityModels} instance. */ public SecurityModels getSecurityModels() { return securityModels; } /** * Gets the enterprise ID used for creating the local engine ID. * @return * an enterprise ID as registered by the IANA (see http://www.iana.org). * @deprecated * Use {@link SNMP4JSettings#getEnterpriseID()} instead. */ public static int getEnterpriseID() { return SNMP4JSettings.getEnterpriseID(); } /** * Sets the IANA enterprise ID to be used for creating local engine ID by * {@link #createLocalEngineID()}. * @param newEnterpriseID * an enterprise ID as registered by the IANA (see http://www.iana.org). * @deprecated * Use {@link SNMP4JSettings#setEnterpriseID(int)} instead. */ public static void setEnterpriseID(int newEnterpriseID) { SNMP4JSettings.setEnterpriseID(newEnterpriseID); } /** * Fire a counter incrementation event. * @param e CounterEvent */ protected void fireIncrementCounter(CounterEvent e) { if (counterSupport != null) { counterSupport.fireIncrementCounter(e); } } /** * Gets the counter support instance that can be used to register for * counter incrementation events. * @return * a {@link CounterSupport} instance that is used to fire * {@link CounterEvent}. */ public CounterSupport getCounterSupport() { return counterSupport; } /** * Sets the counter support instance. By default, the singleton instance * provided by the {@link CounterSupport} instance is used. * @param counterSupport * a {@link CounterSupport} subclass instance. */ public void setCounterSupport(CounterSupport counterSupport) { if (counterSupport == null) { throw new NullPointerException(); } this.counterSupport = counterSupport; } /** * Adds a SNMP engine listener that needs to be informed about changes to * the engine ID cache. * @param l * a {@link SnmpEngineListener} instance. * @since 1.6 */ public synchronized void addSnmpEngineListener(SnmpEngineListener l) { List listeners = snmpEngineListeners; if (listeners == null) { listeners = new ArrayList(); } else { listeners = new ArrayList(snmpEngineListeners); } listeners.add(l); this.snmpEngineListeners = listeners; } /** * Removes a SNMP engine listener. * @param l * a {@link SnmpEngineListener} instance. * @since 1.6 */ public synchronized void removeSnmpEngineListener(SnmpEngineListener l) { List listeners = snmpEngineListeners; if (listeners != null) { listeners = new ArrayList(listeners); listeners.remove(l); this.snmpEngineListeners = listeners; } } /** * Get the number of cached engine IDs. * @return * size of the internal engine ID cache. * @since 2.3.4 */ public int getEngineIdCacheSize() { return engineIDs.size(); } /** * Creates a PDU class that is used to parse incoming SNMP messages. * @param target * the {@code target} parameter must be ignored. * @return * a {@link ScopedPDU} instance by default. * @since 1.9.1 * @deprecated * Use {@link org.snmp4j.util.DefaultPDUFactory#createPDU(MessageProcessingModel, int)} instead. */ public PDU createPDU(Target target) { return incomingPDUFactory.createPDU(target); } /** * Gets the message ID that will be used for the next request to be sent by this message processing model. * @return * the next message ID used by the MPv3. * @since 2.4.3 */ public int getNextMsgID() { return currentMsgID; } /** * Sets the next message ID. According to RFC3412, the message ID should be unique across reboots: * "Values for msgID SHOULD be generated in a manner that avoids re-use * of any outstanding values. Doing so provides protection against some * replay attacks. One possible implementation strategy would be to use * the low-order bits of snmpEngineBoots [RFC3411] as the high-order * portion of the msgID value and a monotonically increasing integer for * the low-order portion of msgID." * * @param nextMsgID * a message ID that has not been used by this SNMP entity yet (preferably also not * used during previous runs). * @since 2.4.3 */ public void setCurrentMsgID(int nextMsgID) { this.currentMsgID = nextMsgID; } /** * Fires a SNMP engine event the registered listeners. * @param engineEvent * the {@code SnmpEngineEvent} instance to fire. * @since 1.6 */ protected void fireEngineChanged(SnmpEngineEvent engineEvent) { if (snmpEngineListeners != null) { List listeners = snmpEngineListeners; for (SnmpEngineListener listener : listeners) { listener.engineChanged(engineEvent); } } } /** * The {@code EngineIdCacheFactory} creates an engine ID cache with upper limit. */ public interface EngineIdCacheFactory { /** * Create a engine ID map with the given maximum capacity. If more than those engine IDs are added, * the eldest engine IDs will be removed from the map before the new one is added. * @param maximumCapacity * the upper limit of the number of engine ID mappings in this map. * @return * the created map. */ Map createEngineIdMap(org.snmp4j.mp.EngineIdCacheSize maximumCapacity); } private static class LimitedCapacityEngineIdCacheFactory implements EngineIdCacheFactory { @Override public Map createEngineIdMap(final org.snmp4j.mp.EngineIdCacheSize cacheSize) { Map map = new HashMap(); return Collections.synchronizedMap(map); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy