org.snmp4j.mp.MPv3 Maven / Gradle / Ivy
/*_############################################################################
_##
_## SNMP4J - MPv3.java
_##
_## Copyright (C) 2003-2024 Frank Fock (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.lang.ref.WeakReference;
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();
public 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 final 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, {@code 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 {
private static final long serialVersionUID = 8698046643337640719L;
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;
}
@Override
public String toString() {
return "CacheEntry{" +
"msgID=" + msgID +
", transactionID=" + transactionID +
", secEngineID=" + OctetString.fromByteArray(secEngineID) +
", secModel=" + secModel +
", secName=" + OctetString.fromByteArray(secName) +
", secLevel=" + secLevel +
", contextEngineID=" + OctetString.fromByteArray(contextEngineID) +
", contextName=" + OctetString.fromByteArray(contextName) +
", secStateReference=" + secStateReference +
", errorCode=" + errorCode +
'}';
}
}
/**
* The {@code Cache} stores state reference information for the MPv3.
*
* @author Frank Fock
* @version 3.4.0
*/
protected static class Cache {
private final Map> entries = new WeakHashMap<>(25);
private final Map> msgIdToPduHandleMapping = new WeakHashMap<>(25);
protected Cache() {
}
/**
* Adds a {@code StateReference} to the cache. The {@code PduHandle} of the supplied entry will be set
* to {@code null} when the new entry is already part of the cache, because the
* cache uses a {@code WeakHashMap} internally which uses the
* {@code 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);
WeakReference pduHandleReference = new WeakReference<>(key);
msgIdToPduHandleMapping.put(entry.getMsgID(), pduHandleReference);
if (entry.getMessageIDs() != null) {
for (MessageID id: entry.getMessageIDs()) {
msgIdToPduHandleMapping.put(id, pduHandleReference);
}
}
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);
if (e != null) {
msgIdToPduHandleMapping.remove(e.getMsgID());
if (e.getMessageIDs() != null) {
for (MessageID messageID : e.getMessageIDs()) {
msgIdToPduHandleMapping.remove(messageID);
if (logger.isDebugEnabled()) {
logger.debug("Removed msgId retry cache sub-entry: " + messageID +
" from msgIdToPduHandleMapping: "+pduHandle);
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("Removed cache entry: " + e + " for "+pduHandle);
}
}
else if (logger.isWarnEnabled()) {
logger.warn("Cache entry for pduHandle "+pduHandle+" prematurely garbage collected");
}
return (e != null);
}
/**
* Gets the cache entry with the supplied ID from the cache.
*
* @param messageID
* a message ID.
* @param removeFoundEntry
* if {@code true}, the found entry will be removed from the cache before it is returned, otherwise
* it is returned only.
* @return a {@link CacheEntry} instance with the given message ID or {@code null} if such an entry cannot be
* found.
* @since 3.4.0
*/
public synchronized StateReference> getEntry(MessageID messageID, boolean removeFoundEntry) {
WeakReference pduHandleReference = msgIdToPduHandleMapping.get(messageID);
if (pduHandleReference != null) {
PduHandle pduHandle = pduHandleReference.get();
if (pduHandle != null) {
StateReference> e = entries.get(pduHandle);
if ((e != null) && (e.isMatchingMessageID(messageID))) {
if (removeFoundEntry) {
deleteEntry(pduHandle);
entries.remove(pduHandle);
e.setPduHandle(pduHandle);
if (logger.isDebugEnabled()) {
logger.debug("Removed cache entry on getEntry: " + e + " for msgID "+messageID);
}
}
return e;
}
}
}
else if (logger.isDebugEnabled()) {
logger.debug("PduHandle for messageID "+messageID+" not found in MPv3.Cache");
}
return null;
}
/**
* Pop the cache entry with the supplied ID from the cache.
*
* @param messageID
* 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(MessageID messageID) {
return getEntry(messageID, true);
}
}
/**
* 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);
protected HeaderData() {}
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());
}
long startPos = message.getPosition();
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, (int) (message.getPosition() - startPos),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(A transportAddress, int maxMessageSize,
int messageProcessingModel, int securityModel,
byte[] securityName, int securityLevel, PDU pdu,
boolean expectResponse, PduHandle sendPduHandle,
BEROutputStream outgoingMessage,
TransportStateReference tmStateReference) throws
IOException {
if (!(pdu instanceof ScopedPDU)) {
String msg = "MPv3 only accepts ScopedPDU instances as pdu parameter";
logger.error(msg);
throw new IllegalArgumentException(msg);
}
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);
break;
}
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 = initHeaderDataFromSecurityLevel(securityLevel);
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();
SecurityStateReference securityStateReference = secModel.newSecurityStateReference();
int status = secModel.generateRequestMessage(messageProcessingModel,
globalDataBuffer.array(),
maxMessageSize,
securityModel,
secEngineID,
securityName,
securityLevel,
scopedPDUInput,
securityParameters,
outgoingMessage,
tmStateReference, securityStateReference);
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(),
securityStateReference,
status));
}
}
return status;
}
private static int initHeaderDataFromSecurityLevel(int securityLevel) {
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;
default:
logger.warn("Unknown security level "+securityLevel+" ignored and set to authPriv(3) instead");
flags = 3;
}
return flags;
}
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());
if (cacheEntry == null) {
if (logger.isDebugEnabled()) {
logger.debug("RFC3412 §7.1 - Preparing response or internal message failed (msgID=" +
stateReference.getMsgID() + ") because cached " +
"information for the msgID could not be found");
}
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 = initHeaderDataFromSecurityLevel(securityLevel);
// 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;
}
@Override
public int prepareDataElements(MessageDispatcher messageDispatcher, A 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 = null;
StateReference> cacheEntry =
cache.getEntry(StateReference.createMessageID(header.getMsgID()), false);
if (cacheEntry != null) {
secStateReference = cacheEntry.getSecurityStateReference();
}
else {
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());
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;
}
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);
secStateReference.setCachedForResponseProcessing(true);
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)) {
cacheEntry = cache.popEntry(StateReference.createMessageID(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("RFC3412 §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);
}
else {
secStateReference.setCachedForResponseProcessing(true);
}
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;
}
/**
* 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.
*/
@Deprecated
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