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

org.snmp4j.agent.mo.snmp.NotificationOriginatorImpl Maven / Gradle / Ivy

There is a newer version: 3.8.1
Show newest version
/*_############################################################################
  _## 
  _##  SNMP4J-Agent 3 - NotificationOriginatorImpl.java  
  _## 
  _##  Copyright (C) 2005-2021  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.util.*;

import org.snmp4j.*;
import org.snmp4j.agent.*;
import org.snmp4j.agent.mo.*;
import org.snmp4j.agent.security.*;
import org.snmp4j.event.*;
import org.snmp4j.smi.*;
import org.snmp4j.log.LogFactory;
import org.snmp4j.log.LogAdapter;
import org.snmp4j.mp.MessageProcessingModel;
import org.snmp4j.agent.mo.snmp.SnmpTargetMIB.SnmpTargetAddrEntryRow;

import java.io.IOException;

import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.transport.TransportType;

import java.net.InetAddress;

/**
 * The {@code NotificationOriginatorImpl} class implements a notification originator application for SNMP4J.
 * See also RFC 3411 for a description of notification originators.
 *
 *
 * @author Frank Fock
 * @version 3.0.0
 */
public class NotificationOriginatorImpl implements NotificationOriginator {

    private static final LogAdapter logger =
            LogFactory.getLogger(NotificationOriginatorImpl.class);

    private Session session;
    private final VACM vacm;
    private final SnmpTargetMIB targetMIB;
    private final SnmpNotificationMIB notificationMIB;
    private SnmpCommunityMIB communityMIB;
    private final SysUpTime sysUpTime;
    private transient List notificationLogListeners;
    private long notificationEventID = 0;

    private static final OctetString EMPTY_CONTEXT_ENGINE_ID = new OctetString();

    /**
     * Creates a notification originator.
     *
     * @param session
     *         the SNMP Session instance to be used to send the notifications/informs.
     * @param vacm
     *         the VACM to be used to check access for notifications.
     * @param sysUpTime
     *         the sysUpTime instance to be used to determine sysUpTime.0 when sending notifications without
     *         specifically specified sysUpTime.
     * @param targetMIB
     *         the SnmpTargetMIB containing notification target information.
     * @param notificationMIB
     *         SnmpNotificationMIB
     */
    public NotificationOriginatorImpl(Session session,
                                      VACM vacm,
                                      SysUpTime sysUpTime,
                                      SnmpTargetMIB targetMIB,
                                      SnmpNotificationMIB notificationMIB) {
        this.session = session;
        this.sysUpTime = sysUpTime;
        this.vacm = vacm;
        this.targetMIB = targetMIB;
        this.notificationMIB = notificationMIB;
    }

    /**
     * Creates a notification originator.
     *
     * @param session
     *         the Snmp instance to be used to send the notifications/informs.
     * @param vacm
     *         the VACM to be used to check access for notifications.
     * @param sysUpTime
     *         the sysUpTime instance to be used to determine sysUpTime.0 when sending notifications without
     *         specifically specified sysUpTime.
     * @param targetMIB
     *         the SnmpTargetMIB containing notification target information.
     * @param notificationMIB
     *         the SnmpNotificationMIB containing notification filtering information.
     * @param communityMIB
     *         the community MIB for coexistence information.
     */
    public NotificationOriginatorImpl(Session session,
                                      VACM vacm,
                                      SysUpTime sysUpTime,
                                      SnmpTargetMIB targetMIB,
                                      SnmpNotificationMIB notificationMIB,
                                      SnmpCommunityMIB communityMIB) {
        this(session, vacm, sysUpTime, targetMIB, notificationMIB);
        this.communityMIB = communityMIB;
    }

    /**
     * Sends notifications (traps) to all appropriate notification targets.
     *
     * @param context
     *         the context name of the context on whose behalf this notification has been generated.
     * @param notificationID
     *         the object ID that uniquely identifies this notification. For SNMPv1 traps, the notification ID has to be
     *         build using the rules provided by RFC 2576.
     * @param vbs
     *         an array of {@code VariableBinding} instances representing the payload of the notification.
     *
     * @return an array of ResponseEvent instances. Since the
     * {@code NotificationOriginator} determines on behalf of the
     * SNMP-NOTIFICATION-MIB contents whether a notification is sent as trap/notification or as inform request, the
     * returned array contains an element for each addressed target, but only a response PDU for inform targets.
     */
    public Object notify(OctetString context, OID notificationID,
                         VariableBinding[] vbs) {
        return notify(context, notificationID, null, vbs);
    }

    private  ResponseEvent sendNotification(A address, Integer32 timeout, Integer32 retries,
                                                                  SnmpTargetAddrEntryRow targetAddrEntryRow,
                                                                  MOTableRow paramsRow, OctetString context,
                                                                  OID notificationID, TimeTicks sysUpTime,
                                                                  VariableBinding[] vbs, int type,
                                                                  long notificationEventID) {
        Integer32 mpModel = (Integer32) paramsRow.getValue(SnmpTargetMIB.idxSnmpTargetParamsMPModel);
        OctetString secName = (OctetString) paramsRow.getValue(SnmpTargetMIB.idxSnmpTargetParamsSecurityName);
        Integer32 secLevel = (Integer32) paramsRow.getValue(SnmpTargetMIB.idxSnmpTargetParamsSecurityLevel);
//    Integer32 secModel = (Integer32)
//        paramsRow.getValue(SnmpTargetMIB.idxSnmpTargetParamsSecurityModel);
        OctetString community = secName;
        try {
            if (secName == null) {
                throw new IllegalArgumentException("Security name must not be null");
            }
            if (communityMIB != null) {
                community = communityMIB.getCommunity(secName, null, context);
            }
            if (address == null) {
                String errMessage = "Null address, notification skipped";
                logger.debug(errMessage);
                throw new IllegalArgumentException(errMessage);
            }
            if ((mpModel.getValue() == MessageProcessingModel.MPv1 ||
                    mpModel.getValue() == MessageProcessingModel.MPv2c) &&
                    (community == null)) {
                throw new IllegalArgumentException("Security name '" + secName +
                        "' is not mapped to any community, notification not sent (RFC 3584 §5.2.3)");
            }
        } catch (IllegalArgumentException iaex) {
            logger.warn("Notification not sent due to configuration error: " + iaex.getMessage());
            //return new ResponseEvent((Snmp)session, address, null, null, notificationID, iaex);
            return null;
        }
        Target t;
        PDU pdu;
        switch (mpModel.getValue()) {
            case MessageProcessingModel.MPv1: {
                t = new CommunityTarget(address, community);
                PDUv1 trap = new PDUv1();
                pdu = trap;
                if (sysUpTime != null) {
                    trap.setTimestamp(sysUpTime.getValue());
                } else {
                    trap.setTimestamp(this.sysUpTime.get().getValue());
                }
                int genericID = SnmpConstants.getGenericTrapID(notificationID);
                if (genericID < 0) {
                    trap.setGenericTrap(6);
                    if ((notificationID.size() > 2) &&
                            (notificationID.get(notificationID.size() - 2) == 0)) {
                        OID enterprise = new OID(notificationID.getValue(), 0, notificationID.size() - 2);
                        trap.setEnterprise(enterprise);
                    } else {
                        OID enterprise = new OID(notificationID.getValue(), 0, notificationID.size() - 1);
                        trap.setEnterprise(enterprise);
                    }
                    trap.setSpecificTrap(notificationID.last());
                } else {
                    trap.setGenericTrap(genericID);
                    trap.setEnterprise(new OID(new int[]{0, 0}));
                }
                if (session instanceof Snmp) {
                    TransportMapping tm =
                            ((Snmp) session).getMessageDispatcher().getTransport(address, TransportType.sender);
                    if ((tm != null) && (tm.getListenAddress() instanceof IpAddress)) {
                        InetAddress localAddress = ((IpAddress) tm.getListenAddress()).getInetAddress();
                        if (localAddress == null) {
                            trap.setAgentAddress(new IpAddress("0.0.0.0"));
                        } else {
                            trap.setAgentAddress(new IpAddress(localAddress));
                        }
                    }
                }
                break;
            }
            case MessageProcessingModel.MPv2c: {
                t = new CommunityTarget(address, community);
                pdu = new PDU();
                break;
            }
            default: {
                byte[] authEngineID =
                        (type == SnmpNotificationMIB.SnmpNotifyTypeEnum.inform) ?
                                new byte[0] : targetMIB.getLocalEngineID();
                // check for TLS
                OID transportDomain = (OID) targetAddrEntryRow.getValue(SnmpTargetMIB.idxSnmpTargetAddrTDomain);
                if (SnmpTlsTmMib.oidSnmpTLSTCPDomain.equals(transportDomain) ||
                        SnmpTlsTmMib.oidSnmpDTLSUDPDomain.equals(transportDomain)) {
                    t = targetAddrEntryRow.getTarget(null, context, address);
                }
                else {
                    t = new UserTarget(address, secName, authEngineID, secLevel.getValue());
                }
                ScopedPDU scopedPdu = new ScopedPDU();
                scopedPdu.setContextName(context);
                // context engine ID is always ID of the local engine if not overwritten
                // by a subclass
                setContextEngineID(scopedPdu, context, notificationID);
                pdu = scopedPdu;
            }
        }
        if (t == null) {
            return null;
        }
        t.setVersion(mpModel.getValue());
        t.setTimeout(timeout.getValue() * 10L);
        t.setRetries(retries.getValue());
        if (mpModel.getValue() != MessageProcessingModel.MPv1) {
            if (sysUpTime != null) {
                pdu.add(new VariableBinding(SnmpConstants.sysUpTime, sysUpTime));
            } else {
                pdu.add(new VariableBinding(SnmpConstants.sysUpTime, this.sysUpTime.get()));
            }
            pdu.add(new VariableBinding(SnmpConstants.snmpTrapOID, notificationID));
        }
        pdu.addAll(vbs);
        pdu.setType((type == SnmpNotificationMIB.SnmpNotifyTypeEnum.inform) ?
                PDU.INFORM : (mpModel.getValue() == MessageProcessingModel.MPv1)
                ? PDU.V1TRAP : PDU.TRAP);
        try {
            OctetString localEngineID = new OctetString();
            OctetString contextEngineID = new OctetString();
            if (pdu instanceof ScopedPDU) {
                localEngineID.setValue(targetMIB.getLocalEngineID());
                contextEngineID = ((ScopedPDU) pdu).getContextEngineID();
            }
            ResponseEvent response = session.send(pdu, t);
            fireNotificationLogEvent(new NotificationLogEvent(this,
                    localEngineID,
                    t,
                    contextEngineID,
                    context, notificationID,
                    sysUpTime,
                    vbs, notificationEventID, true));
            logger.info("Sent notification with ID " + notificationEventID +
                    " " + pdu + " to " + t);
            return response;
        } catch (IOException iox) {
            logger.error("Failed to send notification: " + iox.getMessage(), iox);
        }
        return null;
    }

    /**
     * Sets the context engine ID of the scoped PDU to the local engine ID provided by the {@code targetMIB}
     * member.
     *
     * @param scopedPDU
     *         the scopedPDU to modify.
     * @param context
     *         the context associated with the notification/inform PDU.
     * @param notificationID
     *         the notification ID of the notification/inform PDU.
     *
     * @since 1.2.1
     */
    protected void setContextEngineID(ScopedPDU scopedPDU,
                                      OctetString context,
                                      OID notificationID) {
        scopedPDU.setContextEngineID(new OctetString(targetMIB.getLocalEngineID()));
    }

    private boolean isAccessGranted(MOTableRow addr, MOTableRow paramsRow,
                                    OctetString context,
                                    OID notificationID,
                                    VariableBinding[] vbs) {
        if (!notificationMIB.passesFilter(paramsRow.getIndex(), notificationID, vbs)) {
            if (logger.isInfoEnabled()) {
                logger.info("Notification " + notificationID + " did not pass filter " +
                        paramsRow.getIndex());
            }
            return false;
        }
//    Integer32 mpModel = (Integer32)
//        paramsRow.getValue(SnmpTargetMIB.idxSnmpTargetParamsMPModel);
        OctetString secName = (OctetString)
                paramsRow.getValue(SnmpTargetMIB.idxSnmpTargetParamsSecurityName);
        Integer32 secLevel = (Integer32)
                paramsRow.getValue(SnmpTargetMIB.idxSnmpTargetParamsSecurityLevel);
        Integer32 secModel = (Integer32)
                paramsRow.getValue(SnmpTargetMIB.idxSnmpTargetParamsSecurityModel);
        int status = vacm.isAccessAllowed(context, secName,
                secModel.getValue(), secLevel.getValue(),
                VACM.VIEW_NOTIFY, notificationID);
        for (int i = 0; (status == VACM.VACM_OK) && (i < vbs.length); i++) {
            status = vacm.isAccessAllowed(context, secName,
                    secModel.getValue(), secLevel.getValue(),
                    VACM.VIEW_NOTIFY, vbs[i].getOid());
        }
        return (status == VACM.VACM_OK);
    }

    public Object notify(OctetString context, OID notificationID, TimeTicks sysUpTime, VariableBinding[] vbs) {
        if (logger.isInfoEnabled()) {
            logger.info("Notification " + notificationID + " reported with " +
                    Arrays.asList(vbs) + " for context " + context);
        }
        if (context == null) {
            context = new OctetString();
        }
        List> responses = new LinkedList<>();
        synchronized (this) {
            notificationEventID++;
        }
        OctetString localEngineID = null;
        if ((targetMIB != null) && (targetMIB.getLocalEngineID() != null)) {
            localEngineID = new OctetString(targetMIB.getLocalEngineID());
        }
        fireNotificationLogEvent(new NotificationLogEvent
(this, localEngineID, null, EMPTY_CONTEXT_ENGINE_ID, context, notificationID, sysUpTime, vbs, notificationEventID, false)); for (Iterator it = notificationMIB.getNotifyTable().getModel().iterator(); it.hasNext(); ) { MOTableRow notifyRow = it.next(); OctetString tag = (OctetString) notifyRow.getValue(SnmpNotificationMIB.idxSnmpNotifyTag); Integer32 type = (Integer32) notifyRow.getValue(SnmpNotificationMIB.idxSnmpNotifyType); Collection addresses = targetMIB.getTargetAddrRowsForTag(tag); MOTableRowFilter aFilter = new RowStatus.ActiveRowsFilter(SnmpTargetMIB.idxSnmpTargetAddrRowStatus); for (SnmpTargetAddrEntryRow address : addresses) { if (aFilter.passesFilter(address)) { OctetString params = (OctetString) address.getValue(SnmpTargetMIB.idxSnmpTargetAddrParams); MOTableRow paramsRow = targetMIB.getTargetParamsRow(params); if (paramsRow != null) { if (RowStatus.isRowActive(paramsRow, SnmpTargetMIB.idxSnmpTargetParamsRowStatus)) { if (isAccessGranted(address, paramsRow, context, notificationID, vbs)) { Integer32 timeout = (Integer32) address.getValue(SnmpTargetMIB.idxSnmpTargetAddrTimeout); Integer32 retries = (Integer32) address.getValue(SnmpTargetMIB.idxSnmpTargetAddrRetryCount); ResponseEvent response = sendNotification(address.getAddress(), timeout, retries, address, paramsRow, context, notificationID, sysUpTime, vbs, type.getValue(), notificationEventID); responses.add(response); } else { if (logger.isWarnEnabled()) { logger.warn("Access denied by VACM for " + notificationID); } } } else { logger.warn("Found active target address but corresponding params " + " are not active"); } } else { if (logger.isWarnEnabled()) { logger.warn("Found active target address but not corresponding params row: '" + params + "'"); } } } } } return responses.toArray(new ResponseEvent[0]); } /** * Sets the SNMP session to used by this notification originator for sending notifications. * * @param snmpSession * the Snmp instance to be used to send the notifications/informs. * * @since 1.9.1 */ public void setSession(Session snmpSession) { this.session = snmpSession; } public synchronized void addNotificationLogListener(NotificationLogListener l) { if (notificationLogListeners == null) { notificationLogListeners = new ArrayList<>(2); } notificationLogListeners.add(l); } public synchronized void removeNotificationLogListener(NotificationLogListener l) { if (notificationLogListeners != null) { notificationLogListeners.remove(l); } } protected synchronized void fireNotificationLogEvent(NotificationLogEvent event) { List listeners = notificationLogListeners; if (listeners != null) { for (NotificationLogListener listener : listeners) { listener.notificationLogEvent(event); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy