org.snmp4j.agent.mo.snmp.ProxyForwarderImpl Maven / Gradle / Ivy
/*_############################################################################
_##
_## SNMP4J-Agent 3 - ProxyForwarderImpl.java
_##
_## Copyright (C) 2005-2018 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.agent.mo.snmp;
import java.io.*;
import java.util.*;
import org.snmp4j.*;
import org.snmp4j.agent.*;
import org.snmp4j.agent.mo.*;
import org.snmp4j.agent.mo.snmp.SnmpProxyMIB.*;
import org.snmp4j.agent.mo.snmp.SnmpTargetMIB.*;
import org.snmp4j.agent.request.*;
import org.snmp4j.agent.security.*;
import org.snmp4j.event.*;
import org.snmp4j.log.*;
import org.snmp4j.mp.*;
import org.snmp4j.smi.*;
import org.snmp4j.util.*;
import java.net.InetAddress;
/**
* The {@code ProxyForwarderImpl} class implements a proxy forwarder
* instance as defined by RFC 3413. It is configured through the SNMP-PROXY-MIB
* and SNMP-TARGET-MIB implementations provided on construction. It sends
* notifications through the provided SNMP session.
*
* @author Frank Fock
* @version 3.0.0
*/
public class ProxyForwarderImpl implements ProxyForwarder {
private static final LogAdapter logger =
LogFactory.getLogger(ProxyForwarderImpl.class);
private Session session;
private SnmpProxyMIB proxyMIB;
private SnmpTargetMIB targetMIB;
private transient List counterListeners;
/**
* Creates a {@code ProxyForwarder} implementation based on a SNMP
* session used to send the notifications and a SNMP-PROXY-MIB
* and a SNMP-TARGET-MIB implementation used for its configuration.
*
* @param session
* a SNMP session.
* @param proxyMIB
* a {@code SnmpProxyMIB} implementation with the proxy configuration.
* @param targetMIB
* a {@code SnmpTargetMIB} implementation with the target
* configuration.
*/
public ProxyForwarderImpl(Session session,
SnmpProxyMIB proxyMIB, SnmpTargetMIB targetMIB) {
this.session = session;
this.proxyMIB = proxyMIB;
this.targetMIB = targetMIB;
}
/**
* Forwards a {@code Request} if it matches the criteria defined by the
* SNMP-PROXY-MIB associated with this proxy forwarder.
*
* @param request
* a {@code ProxyForwardRequest} encapsuling the forwarding request.
*
* @return {@code true} if the request has been forwarded,
* {@code false} otherwise.
*/
public boolean forward(ProxyForwardRequest request) {
int pduType = request.getCommandEvent().getPDU().getType();
if (SnmpRequest.getViewType(pduType) == VACM.VIEW_NOTIFY) {
return multipleForward(request);
} else {
ResponseEvent resp = singleForward(request);
if ((resp != null) && (resp.getResponse() != null)) {
PDU respPDU = resp.getResponse();
PDU translatedResponse = DefaultPDUFactory.createPDU(
request.getCommandEvent().getSecurityModel());
if (!translatedResponse.getClass().equals(respPDU.getClass())) {
// not required PDU instance -> copy data
translatedResponse.setType(respPDU.getType());
translatedResponse.addAll(respPDU.toArray());
translatedResponse.setErrorIndex(respPDU.getErrorIndex());
translatedResponse.setErrorStatus(respPDU.getErrorStatus());
} else {
translatedResponse = respPDU;
}
if (translatedResponse.getType() == PDU.RESPONSE) {
translatedResponse.setRequestID(
request.getCommandEvent().getPDU().getRequestID());
if ((translatedResponse instanceof ScopedPDU) &&
(request.getCommandEvent().getPDU() instanceof ScopedPDU)) {
ScopedPDU scopedPDUReq = (ScopedPDU) request.getCommandEvent().getPDU();
ScopedPDU scopedPDUResp = (ScopedPDU) translatedResponse;
scopedPDUResp.setContextEngineID(scopedPDUReq.getContextEngineID());
scopedPDUResp.setContextName(scopedPDUReq.getContextName());
}
}
request.setResponsePDU(translatedResponse);
return true;
}
}
return false;
}
protected List getMatches(ProxyForwardRequest> request) {
List matches =
proxyMIB.getProxyRows(request.getProxyType(),
request.getContextEngineID(),
request.getContext());
for (Iterator it = matches.iterator(); it.hasNext(); ) {
OctetString paramIn = it.next().getSnmpProxyTargetParamsIn();
if (logger.isDebugEnabled()) {
logger.debug("Checking possible match for in parameter: " + paramIn);
}
if (!matchParameters(request, paramIn)) {
it.remove();
}
}
return matches;
}
protected boolean matchParameters(ProxyForwardRequest> request, OctetString paramIn) {
MOTableRow param2Match = targetMIB.getTargetParamsRow(paramIn, true);
if (param2Match != null) {
int mpModel = param2Match.getValue(
SnmpTargetMIB.idxSnmpTargetParamsMPModel).toInt();
int secModel = param2Match.getValue(
SnmpTargetMIB.idxSnmpTargetParamsSecurityModel).toInt();
int secLevel = param2Match.getValue(
SnmpTargetMIB.idxSnmpTargetParamsSecurityLevel).toInt();
OctetString secName = (OctetString) param2Match.getValue(
SnmpTargetMIB.idxSnmpTargetParamsSecurityName);
if (logger.isDebugEnabled()) {
logger.debug("Matching request " + request + " with mpModel=" + mpModel +
", secModel=" + secModel + ", secLevel=" + secLevel +
", secName=" + secName);
}
if ((mpModel == request.getCommandEvent().getMessageProcessingModel()) &&
(secName.equals(request.getSecurityName())) &&
((secModel == 0) ||
(secModel == request.getCommandEvent().getSecurityModel())) &&
(secLevel == request.getCommandEvent().getSecurityLevel())) {
return true;
}
}
return false;
}
protected ResponseEvent singleForward(ProxyForwardRequest request) {
List matches = getMatches(request);
if ((matches == null) || (matches.isEmpty())) {
if (logger.isInfoEnabled()) {
logger.info("No matching proxy entry found for contextEngineID=" +
request.getContextEngineID() +
", context=" + request.getContext() + " and request=" +
request);
}
return null;
}
OctetString outParam = (OctetString)
((MOTableRow)
matches.get(0)).getValue(SnmpProxyMIB.idxSnmpProxySingleTargetOut);
@SuppressWarnings("unchecked")
Target target = (Target)targetMIB.getTarget(outParam, request.getContextEngineID(), request.getContext());
if (target == null) {
if (logger.isInfoEnabled()) {
logger.info("No matching target with name '" + outParam + "'");
}
return null;
}
// forwarding request
if (logger.isInfoEnabled()) {
logger.info("Forwarding proxy request " + request + " to " + target);
}
PDU reqPDU = request.getCommandEvent().getPDU();
PDU pdu = DefaultPDUFactory.createPDU(target, reqPDU.getType());
setScope(request, pdu);
try {
proxyForwardTranslation(request, reqPDU, pdu);
ResponseEvent response;
do {
response = session.send(pdu, target);
if (logger.isInfoEnabled()) {
logger.info("Received proxy response from " +
response.getPeerAddress() +
" is " + response.getResponse());
}
}
while (proxyBackwardTranslation(reqPDU, pdu, response));
return response;
} catch (Exception ex) {
if (logger.isDebugEnabled()) {
ex.printStackTrace();
}
logger.error("Failed to send proxy request to " + target + " because: " +
ex.getMessage());
fireIncrementCounter(new CounterEvent(this, SnmpConstants.snmpProxyDrops));
if (SNMP4JSettings.isForwardRuntimeExceptions()) {
throw new RuntimeException(ex);
}
return null;
}
}
protected boolean proxyBackwardTranslation(PDU reqPDU, PDU pdu, ResponseEvent> response) {
if (response.getResponse() == null) {
return false;
}
PDU resp = response.getResponse();
if ((resp.getErrorStatus() == PDU.tooBig) &&
(reqPDU.getType() != PDU.GETBULK)) {
response.getResponse().clear();
response.getResponse().setErrorStatus(PDU.noError);
response.getResponse().setErrorIndex(0);
return false;
}
if ((resp.getErrorStatus() == PDU.tooBig) &&
(reqPDU.getType() == PDU.GETBULK) && (pdu.getType() == PDU.GETBULK)) {
/**implemented as defined in RFC 3584 4.3.1(3)*/
if (pdu.size() == 1) {
response.getResponse().clear();
response.getResponse().setErrorStatus(PDU.noError);
response.getResponse().setErrorIndex(0);
return false;
} else {
while (pdu.size() > 1) {
pdu.trim();
}
pdu.setType(PDU.GETNEXT);
return true;
}
}
if ((reqPDU instanceof PDUv1) &&
(!(response.getResponse() instanceof PDUv1))) {
boolean resendNeeded = false;
for (int i = 0; i < resp.size(); i++) {
VariableBinding vb = resp.get(i);
if (vb.getVariable() instanceof Counter64) {
OID nextOID = new OID(vb.getOid());
if (nextOID.last() < 65535) {
nextOID.set(nextOID.size() - 1, 65535);
} else {
nextOID.set(nextOID.size() - 1, OID.MAX_SUBID_VALUE);
}
pdu.get(i).setOid(vb.getOid());
resendNeeded = true;
}
}
if (resendNeeded && (reqPDU.getType() != PDU.GETNEXT)) {
throw new IllegalArgumentException(
"GET response with Counter64 cannot be proxied");
} else {
return true;
}
}
return false;
}
/**
* Translates a source PDU into the supplied target PDU. The mapping between
* the source PDU and the target PDU is done as defined by RFC 3584.
*
* @param request
* the proxy forwarding request.
* @param source
* the source PDU instance.
* @param target
* the target PDU instance. The variable bindings of the source will
* replace any VBs of the target instance. If the source PDU cannot
* be converted to the target PDU {@code target} is not changed.
* Instead an intermediate PDU is returned.
*
* @throws IllegalArgumentException
* if {@code source} contains an illegal notification PDU.
*/
protected void proxyForwardTranslation(ProxyForwardRequest> request, PDU source, PDU target)
throws IllegalArgumentException {
target.clear();
target.setType(source.getType());
if (!(target instanceof PDUv1) && !(source instanceof PDUv1)) {
target.setMaxRepetitions(source.getMaxRepetitions());
target.setNonRepeaters(source.getNonRepeaters());
}
if ((source.getType() == PDU.V1TRAP) &&
(source instanceof PDUv1) &&
(!(target instanceof PDUv1))) {
PDUv1 sourceV1 = (PDUv1) source;
target.setType(PDU.NOTIFICATION);
target.add(new VariableBinding(SnmpConstants.sysUpTime,
new TimeTicks(sourceV1.getTimestamp())));
target.add(new VariableBinding(SnmpConstants.snmpTrapOID,
SnmpConstants.getTrapOID(
sourceV1.getEnterprise(),
sourceV1.getGenericTrap(),
sourceV1.getSpecificTrap())));
target.addAll(source.toArray());
target.add(new VariableBinding(SnmpConstants.snmpTrapAddress,
sourceV1.getAgentAddress()));
target.add(new VariableBinding(SnmpConstants.snmpTrapEnterprise,
sourceV1.getEnterprise()));
OctetString community =
new OctetString(request.getCommandEvent().getSecurityName());
target.add(new VariableBinding(SnmpConstants.snmpTrapCommunity,
community));
} else if (((source.getType() == PDU.NOTIFICATION) ||
(source.getType() == PDU.INFORM)) &&
(target instanceof PDUv1)) {
PDUv1 targetV1 = (PDUv1) target;
target.setType(PDU.V1TRAP);
if ((source.size() < 2) ||
(!(source.get(0).getVariable() instanceof TimeTicks)) ||
(!(source.get(1).getVariable() instanceof OID))) {
throw new IllegalArgumentException("Proxy source invalid notification PDU: " +
source);
}
TimeTicks sysUpTime = (TimeTicks) source.get(0).getVariable();
OID trapOID = (OID) source.get(1).getVariable();
int genericID = SnmpConstants.getGenericTrapID(trapOID);
// RFC 3584 Translating
// SNMPv2 notification to SNMPv1 notification parameters
if (genericID < 0) {
targetV1.setGenericTrap(6);
if ((trapOID.size() > 2) && (trapOID.get(trapOID.size() - 2) == 0)) {
targetV1.setSpecificTrap(trapOID.get(trapOID.size() - 1));
OID enterprise = new OID(trapOID);
enterprise.trim(2);
targetV1.setEnterprise(enterprise);
} else if (trapOID.size() > 1) {
targetV1.setSpecificTrap(trapOID.get(trapOID.size() - 1));
OID enterprise = new OID(trapOID);
enterprise.trim(1);
targetV1.setEnterprise(enterprise);
}
} else {
targetV1.setGenericTrap(genericID);
targetV1.setSpecificTrap(0);
}
target.addAll(source.toArray());
if (request.getCommandEvent().getPeerAddress() instanceof IpAddress) {
InetAddress agentAddress = ((IpAddress)
request.getCommandEvent().getPeerAddress()).getInetAddress();
targetV1.setAgentAddress(new IpAddress(agentAddress));
} else {
targetV1.setAgentAddress(new IpAddress("0.0.0.0"));
}
targetV1.setTimestamp(sysUpTime.getValue());
} else {
target.addAll(source.toArray());
}
}
protected boolean multipleForward(ProxyForwardRequest request) {
List matches = getMatches(request);
boolean allOK = true;
for (SnmpProxyRow item : matches) {
OctetString outParam = item.getSnmpProxyMultipleTargetOut();
Set tags = SnmpTagList.getTags(outParam);
if (logger.isDebugEnabled()) {
logger.debug("Proxy multiple targets out with tags " + tags);
}
for (OctetString tag : tags) {
Collection targets = this.targetMIB.getTargetAddrRowsForTag(tag);
for (SnmpTargetAddrEntryRow targetRow : targets) {
Target target = targetRow.getTarget(request.getContextEngineID(), request.getContext(),
request.getCommandEvent().getStateReference().getAddress());
if (target != null) {
try {
PDU reqPDU = request.getCommandEvent().getPDU();
PDU pdu = DefaultPDUFactory.createPDU(target, reqPDU.getType());
setScope(request, pdu);
proxyForwardTranslation(request, reqPDU, pdu);
ResponseEvent> resp = session.send(pdu, target);
if (logger.isInfoEnabled()) {
logger.info("Forwarded " + request.getCommandEvent() +
" to target " + target + " with response " + resp);
}
if (request.getCommandEvent().getPDU().getType() == PDU.INFORM) {
if ((resp.getResponse() == null) ||
(resp.getResponse().getType() == PDU.REPORT) ||
(resp.getResponse().getErrorStatus() != PDU.noError)) {
allOK = false;
}
}
} catch (IOException ex) {
if (logger.isDebugEnabled()) {
ex.printStackTrace();
}
logger.error("Failed to forward request " + request +
" to target " + target);
allOK = false;
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Parameters for target " + targetRow + " not found");
}
}
}
}
}
return allOK;
}
private void setScope(ProxyForwardRequest> request, PDU pdu) {
if (pdu instanceof ScopedPDU) {
ScopedPDU scopedPDU = (ScopedPDU) pdu;
scopedPDU.setContextEngineID(request.getContextEngineID());
scopedPDU.setContextName(request.getContext());
}
}
public synchronized void addCounterListener(CounterListener l) {
if (counterListeners == null) {
counterListeners = new ArrayList<>(2);
}
counterListeners.add(l);
}
public synchronized void removeCounterListener(CounterListener l) {
if (counterListeners != null) {
counterListeners.remove(l);
}
}
protected synchronized void fireIncrementCounter(CounterEvent event) {
List listeners = counterListeners;
if (listeners != null) {
for (CounterListener listener : listeners) {
listener.incrementCounter(event);
}
}
}
/**
* Sets the SNMP session to used by this proxy forwarder for sending
* SNMP messages.
*
* @param snmpSession
* the Snmp instance to be used to send SNMP messages.
*
* @since 1.9.1
*/
public void setSession(Session snmpSession) {
this.session = snmpSession;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy