SnmpConfiguration.java
/*
* This file is part of Waarp Project (named also Waarp or GG).
*
* Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
* tags. See the COPYRIGHT.txt in the distribution for a full listing of
* individual contributors.
*
* All Waarp Project is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Waarp . If not, see <http://www.gnu.org/licenses/>.
*/
package org.waarp.snmp;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.snmp4j.agent.mo.snmp.StorageType;
import org.snmp4j.agent.mo.snmp.TransportDomains;
import org.snmp4j.security.AuthMD5;
import org.snmp4j.security.AuthSHA;
import org.snmp4j.security.Priv3DES;
import org.snmp4j.security.PrivAES128;
import org.snmp4j.security.PrivAES192;
import org.snmp4j.security.PrivAES256;
import org.snmp4j.security.PrivDES;
import org.snmp4j.security.UsmUser;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.TcpAddress;
import org.snmp4j.smi.TransportIpAddress;
import org.snmp4j.smi.UdpAddress;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.utility.ParametersChecker;
import org.waarp.common.xml.XmlDecl;
import org.waarp.common.xml.XmlHash;
import org.waarp.common.xml.XmlType;
import org.waarp.common.xml.XmlUtil;
import org.waarp.common.xml.XmlValue;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* SnmpConfiguration class from XML file
*/
public final class SnmpConfiguration {
/**
* Internal Logger
*/
private static final WaarpLogger logger =
WaarpLoggerFactory.getLogger(SnmpConfiguration.class);
private static final String SNMP_ROOT = "/snmpconfig/";
private static final String SNMP_CONFIG = "config";
private static final String SNMP_LOCAL_ADDRESS = "localaddress";
private static final String SNMP_NBTHREAD = "nbthread";
private static final String SNMP_FILTERED = "filtered";
private static final String SNMP_USETRAP = "usetrap";
private static final String SNMP_TRAPLEVEL = "trapinformlevel";
private static final XmlDecl[] configConfigDecls = {
new XmlDecl(SNMP_LOCAL_ADDRESS, XmlType.STRING, SNMP_LOCAL_ADDRESS, true),
new XmlDecl(XmlType.INTEGER, SNMP_NBTHREAD),
new XmlDecl(XmlType.BOOLEAN, SNMP_FILTERED),
new XmlDecl(XmlType.BOOLEAN, SNMP_USETRAP),
new XmlDecl(XmlType.INTEGER, SNMP_TRAPLEVEL)
};
private static final String SNMP_TARGETS = "targets";
private static final String SNMP_TARGET = "target";
/**
* Static String
*/
static final String NOTIFY = "notify";
/**
* Static String
*/
static final String V3NOTIFY = "v3notify";
/**
* Static String
*/
static final String V2C = "v2c";
/*
* addTargetAddress(org.snmp4j.smi.OctetString name, org.snmp4j.smi.OID transportDomain,
* org.snmp4j.smi.OctetString address, int timeout, int retries, org.snmp4j.smi.OctetString tagList, "notify"
* org.snmp4j.smi.OctetString params, "v3notify"/"v2c" int storageType) permenant
*/
private static final String SNMP_TARGET_NAME = "name";
private static final String SNMP_TARGET_DOMAIN = "domain";
private static final String SNMP_TARGET_ADDRESS = "address";
private static final String SNMP_TARGET_TIMEOUT = "timeout";
private static final String SNMP_TARGET_RETRIES = "retries";
private static final String SNMP_TARGET_ISV2 = "isv2";
private static final XmlDecl[] configTargetDecls = {
new XmlDecl(XmlType.STRING, SNMP_TARGET_NAME),
new XmlDecl(XmlType.STRING, SNMP_TARGET_DOMAIN),
new XmlDecl(XmlType.STRING, SNMP_TARGET_ADDRESS),
new XmlDecl(XmlType.INTEGER, SNMP_TARGET_TIMEOUT),
new XmlDecl(XmlType.INTEGER, SNMP_TARGET_RETRIES),
new XmlDecl(XmlType.BOOLEAN, SNMP_TARGET_ISV2)
};
/*
* org.snmp4j.security.UsmUser.UsmUser(OctetString securityName, OID authenticationProtocol, OctetString
* authenticationPassphrase, OID privacyProtocol, OctetString privacyPassphrase)
*
* Creates a USM user.
*
* Parameters:
*
* -securityName the security name of the user (typically the user name).
*
* -authenticationProtocol the authentication protcol ID to be associated with this user. If set to null, this
* user only supports unauthenticated messages.
*
* -authenticationPassphrase the authentication passphrase. If not null, authenticationProtocol must also be
* not null. RFC3414 §11.2 requires passphrases to have a minimum length of 8 bytes. If the length of
* authenticationPassphrase is less than 8 bytes an IllegalArgumentException is thrown.
*
* -privacyProtocol the privacy protcol ID to be associated with this user. If set to null, this user only
* supports unencrypted messages.
*
* -privacyPassphrase the privacy passphrase. If not null, privacyProtocol must also be not null. RFC3414
* §11.2 requires passphrases to have a minimum length of 8 bytes. If the length of authenticationPassphrase
* is less than 8 bytes an IllegalArgumentException is thrown.
*/
private static final String SNMP_SECURITIES = "securities";
private static final String SNMP_SECURITY = "security";
private static final String SNMP_SECURITY_NAME = "securityname";
private static final String SNMP_SECURITY_AUTH_PROTOCOL =
"securityauthprotocol";
private static final String SNMP_SECURITY_AUTH_PSSPHRASE = "securityauthpass";
private static final String SNMP_SECURITY_PRIV_PROTOCOL =
"securityprivprotocol";
private static final String SNMP_SECURITY_PRIV_PSSPHRASE = "securityprivpass";
private static final XmlDecl[] configSecurityDecls = {
new XmlDecl(XmlType.STRING, SNMP_SECURITY_NAME),
new XmlDecl(XmlType.STRING, SNMP_SECURITY_AUTH_PROTOCOL),
new XmlDecl(XmlType.STRING, SNMP_SECURITY_AUTH_PSSPHRASE),
new XmlDecl(XmlType.STRING, SNMP_SECURITY_PRIV_PROTOCOL),
new XmlDecl(XmlType.STRING, SNMP_SECURITY_PRIV_PSSPHRASE)
};
private static final XmlDecl[] configSNMP = {
new XmlDecl(SNMP_CONFIG, XmlType.XVAL, SNMP_ROOT + SNMP_CONFIG,
configConfigDecls, false),
new XmlDecl(SNMP_SECURITY, XmlType.XVAL,
SNMP_ROOT + SNMP_SECURITIES + '/' + SNMP_SECURITY,
configSecurityDecls, true),
new XmlDecl(SNMP_TARGET, XmlType.XVAL,
SNMP_ROOT + SNMP_TARGETS + '/' + SNMP_TARGET,
configTargetDecls, true)
};
private static XmlHash hashConfig;
/**
* Address from the configuration for the SNMP Agent listening port
*/
static String[] address;
/**
* Number of threads to use in SNMP agent
*/
static int nbThread = 4;
/**
* Do we filter on Targets for SNMP requests
*/
static boolean isFilterAccessEnabled;
/**
* Do we are using Trap or Inform
*/
static boolean isUsingTrap = true;
/**
* Level for Trap/Inform from 0 to 4
*/
static int trapLevel;
/**
* Default address: all in UDP port 161
*/
public static final String DEFAULTADDRESS = "udp:0.0.0.0/161";
private SnmpConfiguration() {
}
/**
* @return True if the configuration successfully load
*/
private static boolean loadConfig() {
XmlValue value = hashConfig.get(SNMP_LOCAL_ADDRESS);
@SuppressWarnings("unchecked")
final List<String> values = (List<String>) value.getList();
final int length = values.size();
if (length == 0) {
address = new String[] { DEFAULTADDRESS };
} else {
address = new String[length];
int nb = 0;
address = values.toArray(address);
final String[] tmp = new String[length];
for (int j = 0; j < length; j++) {
if (ParametersChecker.isNotEmpty(address[j])) {
tmp[nb] = address[j];
nb++;
}
}
if (nb == 0) {
address = new String[] { DEFAULTADDRESS };
} else if (nb < length) {
// less addresses than intended
address = new String[nb];
System.arraycopy(tmp, 0, address, 0, nb);
}
}
value = hashConfig.get(SNMP_NBTHREAD);
if (value != null && !value.isEmpty()) {
nbThread = value.getInteger();
if (nbThread <= 0) {
nbThread = 4;
}
}
value = hashConfig.get(SNMP_FILTERED);
if (value != null && !value.isEmpty()) {
isFilterAccessEnabled = value.getBoolean();
}
value = hashConfig.get(SNMP_USETRAP);
if (value != null && !value.isEmpty()) {
isUsingTrap = value.getBoolean();
}
value = hashConfig.get(SNMP_TRAPLEVEL);
if (value != null && !value.isEmpty()) {
trapLevel = value.getInteger();
}
return true;
}
/**
* List of all UsmUser
*/
static final List<UsmUser> listUsmUser = new ArrayList<UsmUser>();
/**
* Protocols for Security
*/
public enum SecurityProtocolList {
SHA(AuthSHA.ID), MD5(AuthMD5.ID);
public final OID oid;
SecurityProtocolList(final OID oid) {
this.oid = oid;
}
}
/**
* Protocol for Privacy
*/
public enum PrivacyProtocolList {
P3DES(Priv3DES.ID), PAES128(PrivAES128.ID), PAES192(PrivAES192.ID),
PAES256(PrivAES256.ID), PDES(PrivDES.ID);
public final OID oid;
PrivacyProtocolList(final OID oid) {
this.oid = oid;
}
}
/**
* new XmlDecl(XmlType.STRING, SNMP_SECURITY_NAME), new
* XmlDecl(XmlType.STRING, SNMP_SECURITY_AUTH_PROTOCOL),
* new XmlDecl(XmlType.STRING, SNMP_SECURITY_AUTH_PASSPHRASE), new
* XmlDecl(XmlType.STRING,
* SNMP_SECURITY_PRIV_PROTOCOL), new XmlDecl(XmlType.STRING,
* SNMP_SECURITY_PRIV_PASSPHRASE)
*
* @return True if load successfully
*/
private static boolean loadSecurity() {
String securityName;
String securityProtocol;
String securityPassphrase;
String securityPrivProtocol;
String securityPrivPassphrase;
XmlValue value = hashConfig.get(SNMP_SECURITY);
@SuppressWarnings("unchecked")
final List<XmlValue[]> list = (List<XmlValue[]>) value.getList();
for (final XmlValue[] xmlValues : list) {
securityPassphrase = null;
securityPrivPassphrase = null;
final XmlHash subHash = new XmlHash(xmlValues);
value = subHash.get(SNMP_SECURITY_NAME);
if (value == null || value.isEmpty()) {
logger.warn("No Security Name found");
continue;
}
securityName = value.getString();
value = subHash.get(SNMP_SECURITY_AUTH_PROTOCOL);
SecurityProtocolList secprot = null;
if (value != null && !value.isEmpty()) {
securityProtocol = value.getString();
try {
secprot = SecurityProtocolList.valueOf(securityProtocol);
} catch (final IllegalArgumentException e) {
logger.warn("No Security Protocol found for " + securityName);
continue;
}
value = subHash.get(SNMP_SECURITY_AUTH_PSSPHRASE);
if (value == null || value.isEmpty()) {
// not allowed
securityProtocol = null;
} else {
securityPassphrase = value.getString();
}
}
value = subHash.get(SNMP_SECURITY_PRIV_PROTOCOL);
PrivacyProtocolList privprot = null;
if (value != null && !value.isEmpty()) {
securityPrivProtocol = value.getString();
try {
privprot = PrivacyProtocolList.valueOf(securityPrivProtocol);
} catch (final IllegalArgumentException e) {
logger.warn("No Security Private Protocol found for " + securityName);
continue;
}
value = subHash.get(SNMP_SECURITY_PRIV_PSSPHRASE);
if (value == null || value.isEmpty()) {
// not allowed
securityPrivProtocol = null;
} else {
securityPrivPassphrase = value.getString();
}
}
final UsmUser usm = new UsmUser(new OctetString(securityName),
secprot == null? null : secprot.oid,
secprot == null? null :
new OctetString(securityPassphrase),
privprot == null? null : privprot.oid,
privprot == null? null : new OctetString(
securityPrivPassphrase));
listUsmUser.add(usm);
}
return true;
}
private enum TransportDomain {
UdpIpv4(TransportDomains.transportDomainUdpIpv4),
UdpIpv6(TransportDomains.transportDomainUdpIpv6),
UdpIpv4z(TransportDomains.transportDomainUdpIpv4z),
UdpIpv6z(TransportDomains.transportDomainUdpIpv6z),
TcpIpv4(TransportDomains.transportDomainTcpIpv4),
TcpIpv6(TransportDomains.transportDomainTcpIpv6),
TcpIpv4z(TransportDomains.transportDomainTcpIpv4z),
TcpIpv6z(TransportDomains.transportDomainTcpIpv6z);
public final OID oid;
TransportDomain(final OID oid) {
this.oid = oid;
}
}
/**
* Target entry
*/
public static class TargetElement {
public final OctetString name;
public final OID transportDomain;
public final OctetString address;
public final int timeout;
public final int retries;
public final OctetString tagList;
public final OctetString params;
public final int storageType;
/**
* @param name
* @param transportDomain
* @param address
* @param timeout
* @param retries
* @param tagList
* @param params
* @param storageType
*/
private TargetElement(final OctetString name, final OID transportDomain,
final OctetString address, final int timeout,
final int retries, final OctetString tagList,
final OctetString params, final int storageType) {
this.name = name;
this.transportDomain = transportDomain;
this.address = address;
this.timeout = timeout;
this.retries = retries;
this.tagList = tagList;
this.params = params;
this.storageType = storageType;
}
@Override
public String toString() {
return "Name: " + name + " TD: " + transportDomain + " Add: " + address +
" TO: " + timeout + " RT: " + retries + " TL: " + tagList +
" PM: " + params + " ST: " + storageType;
}
}
/**
* List of Target Element
*/
static final List<TargetElement> listTargetElements =
new ArrayList<TargetElement>();
/**
* Do we use SNMP V2c
*/
static boolean hasV2;
/**
* Do we use SNMP V3
*/
static boolean hasV3;
/**
* new XmlDecl(XmlType.STRING, SNMP_TARGET_NAME), free name new
* XmlDecl(XmlType.STRING, SNMP_TARGET_DOMAIN),
* one of (Udp/Tcp)Ipv(4/6)[z] new XmlDecl(XmlType.STRING,
* SNMP_TARGET_ADDRESS), new XmlDecl(XmlType.INTEGER,
* SNMP_TARGET_TIMEOUT), new XmlDecl(XmlType.INTEGER, SNMP_TARGET_RETRIES),
* new XmlDecl(XmlType.BOOLEAN,
* SNMP_TARGET_ISV2) True => v2, else v3
* <p>
* new OctetString("notificationV2c"), TransportDomains.transportDomainUdpIpv4,
* new OctetString( new
* UdpAddress(toAddressV2).getValue()), 200, 1, new OctetString("notify"),
* new
* OctetString("v2c"),
* StorageType.permanent
* <p>
* new OctetString("notificationV3"), TransportDomains.transportDomainUdpIpv4,
* new OctetString( new
* UdpAddress(toAddressV3).getValue()), 200, 1, new OctetString("notify"),
* new
* OctetString("v3notify"),
* StorageType.permanent
*
* @return True if successfully loaded
*/
private static boolean loadTarget() {
String targetName;
String targetDomain;
OID oTargetDomain;
String targetAddress;
int targetTimeout;
int targetRetries;
String targetParams;
XmlValue value = hashConfig.get(SNMP_TARGET);
@SuppressWarnings("unchecked")
final List<XmlValue[]> list = (List<XmlValue[]>) value.getList();
for (final XmlValue[] xmlValues : list) {
final XmlHash subHash = new XmlHash(xmlValues);
value = subHash.get(SNMP_TARGET_NAME);
if (value == null || value.isEmpty()) {
logger.warn("No Target Name found");
continue;
}
targetName = value.getString();
value = subHash.get(SNMP_TARGET_DOMAIN);
if (value == null || value.isEmpty()) {
logger.warn("No Target Domain found for " + targetName);
continue;
}
targetDomain = value.getString();
final TransportDomain domain;
try {
domain = TransportDomain.valueOf(targetDomain);
oTargetDomain = domain.oid;
} catch (final IllegalArgumentException e) {
logger.warn("No Target Domain correctly found for " + targetName);
continue;
}
value = subHash.get(SNMP_TARGET_ADDRESS);
if (value == null || value.isEmpty()) {
logger.warn("No Target Address found for " + targetName);
continue;
}
targetAddress = value.getString();
TransportIpAddress address = null;
try {
switch (domain) {
case UdpIpv4:
case UdpIpv6:
case UdpIpv4z:
case UdpIpv6z:
address = new UdpAddress(targetAddress);
break;
case TcpIpv4:
case TcpIpv6:
case TcpIpv4z:
case TcpIpv6z:
address = new TcpAddress(targetAddress);
break;
}
} catch (final IllegalArgumentException e) {
logger.warn("No Correct Target Address found for " + targetName);
continue;
}
if (address != null) {
logger.debug("Addr: {} {}", address.getClass(), targetAddress);
} else {
return false;
}
value = subHash.get(SNMP_TARGET_TIMEOUT);
if (value == null || value.isEmpty()) {
targetTimeout = 200;
} else {
targetTimeout = value.getInteger();
}
if (targetTimeout <= 100) {
targetTimeout = 100;
}
value = subHash.get(SNMP_TARGET_RETRIES);
if (value == null || value.isEmpty()) {
targetRetries = 1;
} else {
targetRetries = value.getInteger();
}
if (targetRetries <= 0) {
targetRetries = 1;
}
value = subHash.get(SNMP_TARGET_ISV2);
boolean isV2 = true;
if (value == null || value.isEmpty()) {
isV2 = true;
} else {
isV2 = value.getBoolean();
}
if (isV2) {
hasV2 = true;
targetParams = V2C;
} else {
hasV3 = true;
targetParams = V3NOTIFY;
}
final TargetElement element =
new TargetElement(new OctetString(targetName), oTargetDomain,
new OctetString(address.getValue()), targetTimeout,
targetRetries, new OctetString(NOTIFY),
new OctetString(targetParams),
StorageType.permanent);
listTargetElements.add(element);
}
return true;
}
/**
* Initiate the configuration from the xml file for SNMP agent
*
* @param file
*
* @return True if OK
*/
public static boolean setConfigurationFromXml(final File file) {
final Document document;
// Open config file
try {
document = XmlUtil.getNewSaxReader().read(file);
} catch (final DocumentException e) {
logger.error(
"Unable to read the XML Config file: " + file.getAbsolutePath() +
": {}", e.getMessage());
return false;
}
if (document == null) {
logger.error(
"Unable to read the XML Config file: " + file.getAbsolutePath());
return false;
}
XmlValue[] configuration = XmlUtil.read(document, configSNMP);
hashConfig = new XmlHash(configuration);
address = new String[] { DEFAULTADDRESS };
nbThread = 4;
listUsmUser.clear();
listTargetElements.clear();
try {
// Now read the configuration
if (!loadConfig()) {
return false;
}
if (!loadSecurity()) {
return false;
}
if (!loadTarget()) {
return false;
}
} finally {
hashConfig.clear();
hashConfig = null;
configuration = null;
}
return true;
}
}