View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.snmp;
21  
22  import org.dom4j.Document;
23  import org.dom4j.DocumentException;
24  import org.snmp4j.agent.mo.snmp.StorageType;
25  import org.snmp4j.agent.mo.snmp.TransportDomains;
26  import org.snmp4j.security.AuthMD5;
27  import org.snmp4j.security.AuthSHA;
28  import org.snmp4j.security.Priv3DES;
29  import org.snmp4j.security.PrivAES128;
30  import org.snmp4j.security.PrivAES192;
31  import org.snmp4j.security.PrivAES256;
32  import org.snmp4j.security.PrivDES;
33  import org.snmp4j.security.UsmUser;
34  import org.snmp4j.smi.OID;
35  import org.snmp4j.smi.OctetString;
36  import org.snmp4j.smi.TcpAddress;
37  import org.snmp4j.smi.TransportIpAddress;
38  import org.snmp4j.smi.UdpAddress;
39  import org.waarp.common.logging.WaarpLogger;
40  import org.waarp.common.logging.WaarpLoggerFactory;
41  import org.waarp.common.utility.ParametersChecker;
42  import org.waarp.common.xml.XmlDecl;
43  import org.waarp.common.xml.XmlHash;
44  import org.waarp.common.xml.XmlType;
45  import org.waarp.common.xml.XmlUtil;
46  import org.waarp.common.xml.XmlValue;
47  
48  import java.io.File;
49  import java.util.ArrayList;
50  import java.util.List;
51  
52  /**
53   * SnmpConfiguration class from XML file
54   */
55  public final class SnmpConfiguration {
56    /**
57     * Internal Logger
58     */
59    private static final WaarpLogger logger =
60        WaarpLoggerFactory.getLogger(SnmpConfiguration.class);
61  
62    private static final String SNMP_ROOT = "/snmpconfig/";
63  
64    private static final String SNMP_CONFIG = "config";
65  
66    private static final String SNMP_LOCAL_ADDRESS = "localaddress";
67  
68    private static final String SNMP_NBTHREAD = "nbthread";
69  
70    private static final String SNMP_FILTERED = "filtered";
71  
72    private static final String SNMP_USETRAP = "usetrap";
73  
74    private static final String SNMP_TRAPLEVEL = "trapinformlevel";
75  
76    private static final XmlDecl[] configConfigDecls = {
77        new XmlDecl(SNMP_LOCAL_ADDRESS, XmlType.STRING, SNMP_LOCAL_ADDRESS, true),
78        new XmlDecl(XmlType.INTEGER, SNMP_NBTHREAD),
79        new XmlDecl(XmlType.BOOLEAN, SNMP_FILTERED),
80        new XmlDecl(XmlType.BOOLEAN, SNMP_USETRAP),
81        new XmlDecl(XmlType.INTEGER, SNMP_TRAPLEVEL)
82    };
83  
84    private static final String SNMP_TARGETS = "targets";
85  
86    private static final String SNMP_TARGET = "target";
87  
88    /**
89     * Static String
90     */
91    static final String NOTIFY = "notify";
92  
93    /**
94     * Static String
95     */
96    static final String V3NOTIFY = "v3notify";
97  
98    /**
99     * Static String
100    */
101   static final String V2C = "v2c";
102 
103   /*
104    * addTargetAddress(org.snmp4j.smi.OctetString name, org.snmp4j.smi.OID transportDomain,
105    * org.snmp4j.smi.OctetString address, int timeout, int retries, org.snmp4j.smi.OctetString tagList, "notify"
106    * org.snmp4j.smi.OctetString params, "v3notify"/"v2c" int storageType) permenant
107    */
108   private static final String SNMP_TARGET_NAME = "name";
109 
110   private static final String SNMP_TARGET_DOMAIN = "domain";
111 
112   private static final String SNMP_TARGET_ADDRESS = "address";
113 
114   private static final String SNMP_TARGET_TIMEOUT = "timeout";
115 
116   private static final String SNMP_TARGET_RETRIES = "retries";
117 
118   private static final String SNMP_TARGET_ISV2 = "isv2";
119 
120   private static final XmlDecl[] configTargetDecls = {
121       new XmlDecl(XmlType.STRING, SNMP_TARGET_NAME),
122       new XmlDecl(XmlType.STRING, SNMP_TARGET_DOMAIN),
123       new XmlDecl(XmlType.STRING, SNMP_TARGET_ADDRESS),
124       new XmlDecl(XmlType.INTEGER, SNMP_TARGET_TIMEOUT),
125       new XmlDecl(XmlType.INTEGER, SNMP_TARGET_RETRIES),
126       new XmlDecl(XmlType.BOOLEAN, SNMP_TARGET_ISV2)
127   };
128 
129   /*
130    * org.snmp4j.security.UsmUser.UsmUser(OctetString securityName, OID authenticationProtocol, OctetString
131    * authenticationPassphrase, OID privacyProtocol, OctetString privacyPassphrase)
132    *
133    * Creates a USM user.
134    *
135    * Parameters:
136    *
137    * -securityName the security name of the user (typically the user name).
138    *
139    * -authenticationProtocol the authentication protcol ID to be associated with this user. If set to null, this
140    * user only supports unauthenticated messages.
141    *
142    * -authenticationPassphrase the authentication passphrase. If not null, authenticationProtocol must also be
143    * not null. RFC3414 §11.2 requires passphrases to have a minimum length of 8 bytes. If the length of
144    * authenticationPassphrase is less than 8 bytes an IllegalArgumentException is thrown.
145    *
146    * -privacyProtocol the privacy protcol ID to be associated with this user. If set to null, this user only
147    * supports unencrypted messages.
148    *
149    * -privacyPassphrase the privacy passphrase. If not null, privacyProtocol must also be not null. RFC3414
150    * §11.2 requires passphrases to have a minimum length of 8 bytes. If the length of authenticationPassphrase
151    * is less than 8 bytes an IllegalArgumentException is thrown.
152    */
153   private static final String SNMP_SECURITIES = "securities";
154 
155   private static final String SNMP_SECURITY = "security";
156 
157   private static final String SNMP_SECURITY_NAME = "securityname";
158 
159   private static final String SNMP_SECURITY_AUTH_PROTOCOL =
160       "securityauthprotocol";
161 
162   private static final String SNMP_SECURITY_AUTH_PSSPHRASE = "securityauthpass";
163 
164   private static final String SNMP_SECURITY_PRIV_PROTOCOL =
165       "securityprivprotocol";
166 
167   private static final String SNMP_SECURITY_PRIV_PSSPHRASE = "securityprivpass";
168 
169   private static final XmlDecl[] configSecurityDecls = {
170       new XmlDecl(XmlType.STRING, SNMP_SECURITY_NAME),
171       new XmlDecl(XmlType.STRING, SNMP_SECURITY_AUTH_PROTOCOL),
172       new XmlDecl(XmlType.STRING, SNMP_SECURITY_AUTH_PSSPHRASE),
173       new XmlDecl(XmlType.STRING, SNMP_SECURITY_PRIV_PROTOCOL),
174       new XmlDecl(XmlType.STRING, SNMP_SECURITY_PRIV_PSSPHRASE)
175   };
176 
177   private static final XmlDecl[] configSNMP = {
178       new XmlDecl(SNMP_CONFIG, XmlType.XVAL, SNMP_ROOT + SNMP_CONFIG,
179                   configConfigDecls, false),
180       new XmlDecl(SNMP_SECURITY, XmlType.XVAL,
181                   SNMP_ROOT + SNMP_SECURITIES + '/' + SNMP_SECURITY,
182                   configSecurityDecls, true),
183       new XmlDecl(SNMP_TARGET, XmlType.XVAL,
184                   SNMP_ROOT + SNMP_TARGETS + '/' + SNMP_TARGET,
185                   configTargetDecls, true)
186   };
187 
188   private static XmlHash hashConfig;
189   /**
190    * Address from the configuration for the SNMP Agent listening port
191    */
192   static String[] address;
193   /**
194    * Number of threads to use in SNMP agent
195    */
196   static int nbThread = 4;
197   /**
198    * Do we filter on Targets for SNMP requests
199    */
200   static boolean isFilterAccessEnabled;
201   /**
202    * Do we are using Trap or Inform
203    */
204   static boolean isUsingTrap = true;
205   /**
206    * Level for Trap/Inform from 0 to 4
207    */
208   static int trapLevel;
209   /**
210    * Default address: all in UDP port 161
211    */
212   public static final String DEFAULTADDRESS = "udp:0.0.0.0/161";
213 
214   private SnmpConfiguration() {
215   }
216 
217   /**
218    * @return True if the configuration successfully load
219    */
220   private static boolean loadConfig() {
221     XmlValue value = hashConfig.get(SNMP_LOCAL_ADDRESS);
222     @SuppressWarnings("unchecked")
223     final List<String> values = (List<String>) value.getList();
224     final int length = values.size();
225     if (length == 0) {
226       address = new String[] { DEFAULTADDRESS };
227     } else {
228       address = new String[length];
229       int nb = 0;
230       address = values.toArray(address);
231       final String[] tmp = new String[length];
232       for (int j = 0; j < length; j++) {
233         if (ParametersChecker.isNotEmpty(address[j])) {
234           tmp[nb] = address[j];
235           nb++;
236         }
237       }
238       if (nb == 0) {
239         address = new String[] { DEFAULTADDRESS };
240       } else if (nb < length) {
241         // less addresses than intended
242         address = new String[nb];
243         System.arraycopy(tmp, 0, address, 0, nb);
244       }
245     }
246     value = hashConfig.get(SNMP_NBTHREAD);
247     if (value != null && !value.isEmpty()) {
248       nbThread = value.getInteger();
249       if (nbThread <= 0) {
250         nbThread = 4;
251       }
252     }
253     value = hashConfig.get(SNMP_FILTERED);
254     if (value != null && !value.isEmpty()) {
255       isFilterAccessEnabled = value.getBoolean();
256     }
257     value = hashConfig.get(SNMP_USETRAP);
258     if (value != null && !value.isEmpty()) {
259       isUsingTrap = value.getBoolean();
260     }
261     value = hashConfig.get(SNMP_TRAPLEVEL);
262     if (value != null && !value.isEmpty()) {
263       trapLevel = value.getInteger();
264     }
265     return true;
266   }
267 
268   /**
269    * List of all UsmUser
270    */
271   static final List<UsmUser> listUsmUser = new ArrayList<UsmUser>();
272 
273   /**
274    * Protocols for Security
275    */
276   public enum SecurityProtocolList {
277     SHA(AuthSHA.ID), MD5(AuthMD5.ID);
278 
279     public final OID oid;
280 
281     SecurityProtocolList(final OID oid) {
282       this.oid = oid;
283     }
284   }
285 
286   /**
287    * Protocol for Privacy
288    */
289   public enum PrivacyProtocolList {
290     P3DES(Priv3DES.ID), PAES128(PrivAES128.ID), PAES192(PrivAES192.ID),
291     PAES256(PrivAES256.ID), PDES(PrivDES.ID);
292 
293     public final OID oid;
294 
295     PrivacyProtocolList(final OID oid) {
296       this.oid = oid;
297     }
298   }
299 
300   /**
301    * new XmlDecl(XmlType.STRING, SNMP_SECURITY_NAME), new
302    * XmlDecl(XmlType.STRING, SNMP_SECURITY_AUTH_PROTOCOL),
303    * new XmlDecl(XmlType.STRING, SNMP_SECURITY_AUTH_PASSPHRASE), new
304    * XmlDecl(XmlType.STRING,
305    * SNMP_SECURITY_PRIV_PROTOCOL), new XmlDecl(XmlType.STRING,
306    * SNMP_SECURITY_PRIV_PASSPHRASE)
307    *
308    * @return True if load successfully
309    */
310   private static boolean loadSecurity() {
311     String securityName;
312     String securityProtocol;
313     String securityPassphrase;
314     String securityPrivProtocol;
315     String securityPrivPassphrase;
316     XmlValue value = hashConfig.get(SNMP_SECURITY);
317     @SuppressWarnings("unchecked")
318     final List<XmlValue[]> list = (List<XmlValue[]>) value.getList();
319     for (final XmlValue[] xmlValues : list) {
320       securityPassphrase = null;
321       securityPrivPassphrase = null;
322       final XmlHash subHash = new XmlHash(xmlValues);
323       value = subHash.get(SNMP_SECURITY_NAME);
324       if (value == null || value.isEmpty()) {
325         logger.warn("No Security Name found");
326         continue;
327       }
328       securityName = value.getString();
329       value = subHash.get(SNMP_SECURITY_AUTH_PROTOCOL);
330       SecurityProtocolList secprot = null;
331       if (value != null && !value.isEmpty()) {
332         securityProtocol = value.getString();
333         try {
334           secprot = SecurityProtocolList.valueOf(securityProtocol);
335         } catch (final IllegalArgumentException e) {
336           logger.warn("No Security Protocol found for " + securityName);
337           continue;
338         }
339         value = subHash.get(SNMP_SECURITY_AUTH_PSSPHRASE);
340         if (value == null || value.isEmpty()) {
341           // not allowed
342           securityProtocol = null;
343         } else {
344           securityPassphrase = value.getString();
345         }
346       }
347       value = subHash.get(SNMP_SECURITY_PRIV_PROTOCOL);
348       PrivacyProtocolList privprot = null;
349       if (value != null && !value.isEmpty()) {
350         securityPrivProtocol = value.getString();
351         try {
352           privprot = PrivacyProtocolList.valueOf(securityPrivProtocol);
353         } catch (final IllegalArgumentException e) {
354           logger.warn("No Security Private Protocol found for " + securityName);
355           continue;
356         }
357         value = subHash.get(SNMP_SECURITY_PRIV_PSSPHRASE);
358         if (value == null || value.isEmpty()) {
359           // not allowed
360           securityPrivProtocol = null;
361         } else {
362           securityPrivPassphrase = value.getString();
363         }
364       }
365       final UsmUser usm = new UsmUser(new OctetString(securityName),
366                                       secprot == null? null : secprot.oid,
367                                       secprot == null? null :
368                                           new OctetString(securityPassphrase),
369                                       privprot == null? null : privprot.oid,
370                                       privprot == null? null : new OctetString(
371                                           securityPrivPassphrase));
372       listUsmUser.add(usm);
373     }
374     return true;
375   }
376 
377   private enum TransportDomain {
378     UdpIpv4(TransportDomains.transportDomainUdpIpv4),
379     UdpIpv6(TransportDomains.transportDomainUdpIpv6),
380     UdpIpv4z(TransportDomains.transportDomainUdpIpv4z),
381     UdpIpv6z(TransportDomains.transportDomainUdpIpv6z),
382     TcpIpv4(TransportDomains.transportDomainTcpIpv4),
383     TcpIpv6(TransportDomains.transportDomainTcpIpv6),
384     TcpIpv4z(TransportDomains.transportDomainTcpIpv4z),
385     TcpIpv6z(TransportDomains.transportDomainTcpIpv6z);
386 
387     public final OID oid;
388 
389     TransportDomain(final OID oid) {
390       this.oid = oid;
391     }
392   }
393 
394   /**
395    * Target entry
396    */
397   public static class TargetElement {
398     public final OctetString name;
399 
400     public final OID transportDomain;
401 
402     public final OctetString address;
403 
404     public final int timeout;
405 
406     public final int retries;
407 
408     public final OctetString tagList;
409 
410     public final OctetString params;
411 
412     public final int storageType;
413 
414     /**
415      * @param name
416      * @param transportDomain
417      * @param address
418      * @param timeout
419      * @param retries
420      * @param tagList
421      * @param params
422      * @param storageType
423      */
424     private TargetElement(final OctetString name, final OID transportDomain,
425                           final OctetString address, final int timeout,
426                           final int retries, final OctetString tagList,
427                           final OctetString params, final int storageType) {
428       this.name = name;
429       this.transportDomain = transportDomain;
430       this.address = address;
431       this.timeout = timeout;
432       this.retries = retries;
433       this.tagList = tagList;
434       this.params = params;
435       this.storageType = storageType;
436     }
437 
438     @Override
439     public String toString() {
440       return "Name: " + name + " TD: " + transportDomain + " Add: " + address +
441              " TO: " + timeout + " RT: " + retries + " TL: " + tagList +
442              " PM: " + params + " ST: " + storageType;
443     }
444   }
445 
446   /**
447    * List of Target Element
448    */
449   static final List<TargetElement> listTargetElements =
450       new ArrayList<TargetElement>();
451   /**
452    * Do we use SNMP V2c
453    */
454   static boolean hasV2;
455   /**
456    * Do we use SNMP V3
457    */
458   static boolean hasV3;
459 
460   /**
461    * new XmlDecl(XmlType.STRING, SNMP_TARGET_NAME), free name new
462    * XmlDecl(XmlType.STRING, SNMP_TARGET_DOMAIN),
463    * one of (Udp/Tcp)Ipv(4/6)[z] new XmlDecl(XmlType.STRING,
464    * SNMP_TARGET_ADDRESS), new XmlDecl(XmlType.INTEGER,
465    * SNMP_TARGET_TIMEOUT), new XmlDecl(XmlType.INTEGER, SNMP_TARGET_RETRIES),
466    * new XmlDecl(XmlType.BOOLEAN,
467    * SNMP_TARGET_ISV2) True => v2, else v3
468    * <p>
469    * new OctetString("notificationV2c"), TransportDomains.transportDomainUdpIpv4,
470    * new OctetString( new
471    * UdpAddress(toAddressV2).getValue()), 200, 1, new OctetString("notify"),
472    * new
473    * OctetString("v2c"),
474    * StorageType.permanent
475    * <p>
476    * new OctetString("notificationV3"), TransportDomains.transportDomainUdpIpv4,
477    * new OctetString( new
478    * UdpAddress(toAddressV3).getValue()), 200, 1, new OctetString("notify"),
479    * new
480    * OctetString("v3notify"),
481    * StorageType.permanent
482    *
483    * @return True if successfully loaded
484    */
485   private static boolean loadTarget() {
486     String targetName;
487     String targetDomain;
488     OID oTargetDomain;
489     String targetAddress;
490     int targetTimeout;
491     int targetRetries;
492     String targetParams;
493     XmlValue value = hashConfig.get(SNMP_TARGET);
494     @SuppressWarnings("unchecked")
495     final List<XmlValue[]> list = (List<XmlValue[]>) value.getList();
496     for (final XmlValue[] xmlValues : list) {
497       final XmlHash subHash = new XmlHash(xmlValues);
498       value = subHash.get(SNMP_TARGET_NAME);
499       if (value == null || value.isEmpty()) {
500         logger.warn("No Target Name found");
501         continue;
502       }
503       targetName = value.getString();
504       value = subHash.get(SNMP_TARGET_DOMAIN);
505       if (value == null || value.isEmpty()) {
506         logger.warn("No Target Domain found for " + targetName);
507         continue;
508       }
509       targetDomain = value.getString();
510       final TransportDomain domain;
511       try {
512         domain = TransportDomain.valueOf(targetDomain);
513         oTargetDomain = domain.oid;
514       } catch (final IllegalArgumentException e) {
515         logger.warn("No Target Domain correctly found for " + targetName);
516         continue;
517       }
518       value = subHash.get(SNMP_TARGET_ADDRESS);
519       if (value == null || value.isEmpty()) {
520         logger.warn("No Target Address found for " + targetName);
521         continue;
522       }
523       targetAddress = value.getString();
524       TransportIpAddress address = null;
525       try {
526         switch (domain) {
527           case UdpIpv4:
528           case UdpIpv6:
529           case UdpIpv4z:
530           case UdpIpv6z:
531             address = new UdpAddress(targetAddress);
532             break;
533           case TcpIpv4:
534           case TcpIpv6:
535           case TcpIpv4z:
536           case TcpIpv6z:
537             address = new TcpAddress(targetAddress);
538             break;
539         }
540       } catch (final IllegalArgumentException e) {
541         logger.warn("No Correct Target Address found for " + targetName);
542         continue;
543       }
544       if (address != null) {
545         logger.debug("Addr: {} {}", address.getClass(), targetAddress);
546       } else {
547         return false;
548       }
549       value = subHash.get(SNMP_TARGET_TIMEOUT);
550       if (value == null || value.isEmpty()) {
551         targetTimeout = 200;
552       } else {
553         targetTimeout = value.getInteger();
554       }
555       if (targetTimeout <= 100) {
556         targetTimeout = 100;
557       }
558       value = subHash.get(SNMP_TARGET_RETRIES);
559       if (value == null || value.isEmpty()) {
560         targetRetries = 1;
561       } else {
562         targetRetries = value.getInteger();
563       }
564       if (targetRetries <= 0) {
565         targetRetries = 1;
566       }
567       value = subHash.get(SNMP_TARGET_ISV2);
568       boolean isV2 = true;
569       if (value == null || value.isEmpty()) {
570         isV2 = true;
571       } else {
572         isV2 = value.getBoolean();
573       }
574       if (isV2) {
575         hasV2 = true;
576         targetParams = V2C;
577       } else {
578         hasV3 = true;
579         targetParams = V3NOTIFY;
580       }
581       final TargetElement element =
582           new TargetElement(new OctetString(targetName), oTargetDomain,
583                             new OctetString(address.getValue()), targetTimeout,
584                             targetRetries, new OctetString(NOTIFY),
585                             new OctetString(targetParams),
586                             StorageType.permanent);
587       listTargetElements.add(element);
588     }
589     return true;
590   }
591 
592   /**
593    * Initiate the configuration from the xml file for SNMP agent
594    *
595    * @param file
596    *
597    * @return True if OK
598    */
599   public static boolean setConfigurationFromXml(final File file) {
600     final Document document;
601     // Open config file
602     try {
603       document = XmlUtil.getNewSaxReader().read(file);
604     } catch (final DocumentException e) {
605       logger.error(
606           "Unable to read the XML Config file: " + file.getAbsolutePath() +
607           ": {}", e.getMessage());
608       return false;
609     }
610     if (document == null) {
611       logger.error(
612           "Unable to read the XML Config file: " + file.getAbsolutePath());
613       return false;
614     }
615     XmlValue[] configuration = XmlUtil.read(document, configSNMP);
616     hashConfig = new XmlHash(configuration);
617     address = new String[] { DEFAULTADDRESS };
618     nbThread = 4;
619     listUsmUser.clear();
620     listTargetElements.clear();
621     try {
622       // Now read the configuration
623       if (!loadConfig()) {
624         return false;
625       }
626       if (!loadSecurity()) {
627         return false;
628       }
629       if (!loadTarget()) {
630         return false;
631       }
632     } finally {
633       hashConfig.clear();
634       hashConfig = null;
635       configuration = null;
636     }
637     return true;
638   }
639 }