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.snmp4j.TransportMapping;
23  import org.snmp4j.agent.BaseAgent;
24  import org.snmp4j.agent.CommandProcessor;
25  import org.snmp4j.agent.DuplicateRegistrationException;
26  import org.snmp4j.agent.MOGroup;
27  import org.snmp4j.agent.mo.snmp.RowStatus;
28  import org.snmp4j.agent.mo.snmp.SNMPv2MIB;
29  import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB;
30  import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB.SnmpCommunityEntryRow;
31  import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB;
32  import org.snmp4j.agent.mo.snmp.SnmpTargetMIB;
33  import org.snmp4j.agent.mo.snmp.StorageType;
34  import org.snmp4j.agent.mo.snmp.VacmMIB;
35  import org.snmp4j.agent.security.MutableVACM;
36  import org.snmp4j.mp.MPv3;
37  import org.snmp4j.mp.MessageProcessingModel;
38  import org.snmp4j.mp.SnmpConstants;
39  import org.snmp4j.security.SecurityLevel;
40  import org.snmp4j.security.SecurityModel;
41  import org.snmp4j.security.USM;
42  import org.snmp4j.security.UsmUser;
43  import org.snmp4j.smi.Address;
44  import org.snmp4j.smi.GenericAddress;
45  import org.snmp4j.smi.Integer32;
46  import org.snmp4j.smi.OID;
47  import org.snmp4j.smi.OctetString;
48  import org.snmp4j.smi.Variable;
49  import org.snmp4j.smi.VariableBinding;
50  import org.snmp4j.transport.TransportMappings;
51  import org.snmp4j.util.ThreadPool;
52  import org.snmp4j.util.WorkerPool;
53  import org.waarp.common.logging.SysErrLogger;
54  import org.waarp.common.logging.WaarpLogger;
55  import org.waarp.common.logging.WaarpLoggerFactory;
56  import org.waarp.snmp.SnmpConfiguration.TargetElement;
57  import org.waarp.snmp.interf.WaarpInterfaceMib;
58  import org.waarp.snmp.interf.WaarpInterfaceMib.TrapLevel;
59  import org.waarp.snmp.interf.WaarpInterfaceMonitor;
60  
61  import java.io.File;
62  import java.io.IOException;
63  import java.util.List;
64  
65  /**
66   * This Agent contains some functionalities for running a version 2c and 3 of
67   * SNMP agent.
68   */
69  public class WaarpSnmpAgent extends BaseAgent {
70    /**
71     * Internal Logger
72     */
73    private static final WaarpLogger logger =
74        WaarpLoggerFactory.getLogger(WaarpSnmpAgent.class);
75  
76    private String[] address = { SnmpConfiguration.DEFAULTADDRESS };
77  
78    private final boolean isFilterAccessEnabled;
79  
80    private boolean useTrap = true;
81  
82    private int trapLevel;
83  
84    private final List<UsmUser> listUsmUser;
85  
86    private final List<TargetElement> listTargetElements;
87  
88    private final boolean hasV2;
89  
90    private final boolean hasV3;
91  
92    private final long systemTimeStart = System.currentTimeMillis();
93  
94    private final WorkerPool workerPool;
95    /**
96     * The associated monitor with this Agent
97     */
98    private WaarpInterfaceMonitor monitor;
99    /**
100    * The associated MIB with this Agent
101    */
102   private WaarpInterfaceMib mib;
103 
104   /**
105    * @param configurationFile XML format
106    * @param monitor the associated monitor
107    * @param mib the associated MIB
108    *
109    * @throws IllegalArgumentException
110    */
111   public WaarpSnmpAgent(final File configurationFile,
112                         final WaarpInterfaceMonitor monitor,
113                         final WaarpInterfaceMib mib)
114       throws IllegalArgumentException {
115     /*
116      * Creates a base agent with boot-counter, config file, and a CommandProcessor for processing SNMP requests.
117      *
118      * Parameters:
119      *
120      * These files does not exist and are not used but has to be specified. Read snmp4j docs for more info
121      *
122      * "bootCounterFile" - a file with serialized boot-counter information (read/write). If the file does not
123      * exist it is created on shutdown of the agent.
124      *
125      * "configFile" - a file with serialized configuration information (read/write). If the file does not exist it
126      * is created on shutdown of the agent.
127      *
128      * "commandProcessor" - the CommandProcessor instance that handles the SNMP requests.
129      */
130     super(new File(configurationFile.getParentFile(), "dummyConf.agent"),
131           new File(configurationFile.getParentFile(), "dummyBootCounter.agent"),
132           new CommandProcessor(new OctetString(MPv3.createLocalEngineID())));
133     if (!SnmpConfiguration.setConfigurationFromXml(configurationFile)) {
134       throw new IllegalArgumentException("Cannot load configuration");
135     }
136     address = SnmpConfiguration.address;
137     final int nbThread = SnmpConfiguration.nbThread;
138     isFilterAccessEnabled = SnmpConfiguration.isFilterAccessEnabled;
139     useTrap = SnmpConfiguration.isUsingTrap;
140     setTrapLevel(SnmpConfiguration.trapLevel);
141     listUsmUser = SnmpConfiguration.listUsmUser;
142     listTargetElements = SnmpConfiguration.listTargetElements;
143     hasV2 = SnmpConfiguration.hasV2;
144     hasV3 = SnmpConfiguration.hasV3;
145 
146     logger.debug("SNMP Configuration loaded: {}:{}", address[0], nbThread);
147     workerPool = ThreadPool.create("SnmpRequestPool", nbThread);
148     agent.setWorkerPool(workerPool);
149     setMonitor(monitor);
150     getMonitor().setAgent(this);
151     setMib(mib);
152     getMib().setAgent(this);
153   }
154 
155   /**
156    * @return the monitor
157    */
158   public final WaarpInterfaceMonitor getMonitor() {
159     return monitor;
160   }
161 
162   /**
163    * @return the mib
164    */
165   public final WaarpInterfaceMib getMib() {
166     return mib;
167   }
168 
169   /**
170    * @return the uptime in ms
171    */
172   public final long getUptime() {
173     return getSnmpv2MIB().getUpTime().toMilliseconds();
174   }
175 
176   /**
177    * @return the uptime but in System time in ms
178    */
179   public final long getUptimeSystemTime() {
180     return systemTimeStart;
181   }
182 
183   /**
184    * Register additional managed objects at the agent's server.
185    */
186   @Override
187   protected final void registerManagedObjects() {
188     logger.debug("Registers");
189     try {
190       getMib().registerMOs(server, null);
191     } catch (final DuplicateRegistrationException e) {
192       logger.error("Cannot register Mib", e);
193     }
194   }
195 
196   /**
197    * Unregister the basic MIB modules from the agent's MOServer.
198    */
199   @Override
200   protected final void unregisterManagedObjects() {
201     logger.debug("Unregisters");
202     getMib().unregisterMOs(server, null);
203   }
204 
205   /**
206    * @param moGroup
207    */
208   public final void unregisterManagedObject(final MOGroup moGroup) {
209     logger.debug("Unregister {}", moGroup);
210     moGroup.unregisterMOs(server, getContext(moGroup));
211   }
212 
213   /**
214    * Adds all the necessary initial users to the USM. Only applicable to SNMP
215    * V3
216    */
217   @Override
218   protected final void addUsmUser(final USM usm) {
219     for (final UsmUser userlist : listUsmUser) {
220       logger.debug("User: {}", userlist);
221       usm.addUser(userlist.getSecurityName(), usm.getLocalEngineID(), userlist);
222     }
223     final UsmUser usernotify =
224         new UsmUser(new OctetString(SnmpConfiguration.V3NOTIFY), null, null,
225                     null, null);
226     usm.addUser(usernotify.getSecurityName(), null, usernotify);
227   }
228 
229   /**
230    * Adds initial notification targets and filters.
231    */
232   @Override
233   protected final void addNotificationTargets(final SnmpTargetMIB targetMIB,
234                                               final SnmpNotificationMIB notificationMIB) {
235     targetMIB.addDefaultTDomains();
236 
237     for (final TargetElement element : listTargetElements) {
238       logger.debug("AddTarget: {}", element);
239       targetMIB.addTargetAddress(element.name, element.transportDomain,
240                                  element.address, element.timeout,
241                                  element.retries, element.tagList,
242                                  element.params, element.storageType);
243     }
244     /*
245      * Example
246      *
247      * targetMIB.addTargetAddress(new OctetString("notificationV2c"), TransportDomains.transportDomainUdpIpv4, new
248      * OctetString(new UdpAddress("127.0.0.1/162").getValue()), 200, 1, new OctetString("notify"), new
249      * OctetString("v2c"), StorageType.permanent); targetMIB.addTargetAddress(new OctetString("notificationV3"),
250      * TransportDomains.transportDomainUdpIpv4, new OctetString(new UdpAddress("127.0.0.1/1162").getValue()), 200,
251      * 1, new OctetString("notify"), new OctetString("v3notify"), StorageType.permanent);
252      */
253     logger.debug("HasV2: {} HasV3: {}", hasV2, hasV3);
254     if (hasV2) {
255       targetMIB.addTargetParams(new OctetString(SnmpConfiguration.V2C),
256                                 MessageProcessingModel.MPv2c,
257                                 SecurityModel.SECURITY_MODEL_SNMPv2c,
258                                 new OctetString("cpublic"),
259                                 SecurityLevel.AUTH_PRIV, StorageType.permanent);
260     }
261     if (hasV3) {
262       targetMIB.addTargetParams(new OctetString(SnmpConfiguration.V3NOTIFY),
263                                 MessageProcessingModel.MPv3,
264                                 SecurityModel.SECURITY_MODEL_USM,
265                                 new OctetString("v3notify"),
266                                 SecurityLevel.NOAUTH_NOPRIV,
267                                 StorageType.permanent);
268     }
269     int trapOrInform = SnmpNotificationMIB.SnmpNotifyTypeEnum.inform;
270     if (useTrap) {
271       trapOrInform = SnmpNotificationMIB.SnmpNotifyTypeEnum.trap;
272     }
273     notificationMIB.addNotifyEntry(new OctetString("default"),
274                                    new OctetString(SnmpConfiguration.NOTIFY),
275                                    trapOrInform, StorageType.permanent);
276   }
277 
278   /**
279    * Minimal View based Access Control
280    * <p>
281    * http://www.faqs.org/rfcs/rfc2575.html
282    */
283   @Override
284   protected final void addViews(final VacmMIB vacm) {
285     vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv1,
286                   new OctetString("cpublic"), new OctetString("v1v2group"),
287                   StorageType.nonVolatile);
288     vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c,
289                   new OctetString("cpublic"), new OctetString("v1v2group"),
290                   StorageType.nonVolatile);
291     vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, new OctetString("v3notify"),
292                   new OctetString("v3group"), StorageType.nonVolatile);
293 
294     for (final UsmUser user : listUsmUser) {
295       logger.debug("Groups: {} Restricted? {}", user.getSecurityName(),
296                    user.getPrivacyProtocol() == null);
297       if (user.getPrivacyProtocol() == null) {
298         vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
299                       new OctetString(user.getSecurityName()),
300                       new OctetString("v3restricted"), StorageType.nonVolatile);
301       } else {
302         vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
303                       new OctetString(user.getSecurityName()),
304                       new OctetString("v3group"), StorageType.nonVolatile);
305       }
306     }
307 
308     vacm.addAccess(new OctetString("v1v2group"), new OctetString("public"),
309                    SecurityModel.SECURITY_MODEL_ANY,
310                    SecurityLevel.NOAUTH_NOPRIV, MutableVACM.VACM_MATCH_EXACT,
311                    new OctetString("fullReadView"),
312                    new OctetString("fullWriteView"),
313                    new OctetString("fullNotifyView"), StorageType.nonVolatile);
314     vacm.addAccess(new OctetString("v3group"), new OctetString(),
315                    SecurityModel.SECURITY_MODEL_USM, SecurityLevel.AUTH_PRIV,
316                    MutableVACM.VACM_MATCH_EXACT,
317                    new OctetString("fullReadView"),
318                    new OctetString("fullWriteView"),
319                    new OctetString("fullNotifyView"), StorageType.nonVolatile);
320     vacm.addAccess(new OctetString("v3restricted"), new OctetString(),
321                    SecurityModel.SECURITY_MODEL_USM,
322                    SecurityLevel.NOAUTH_NOPRIV, MutableVACM.VACM_MATCH_EXACT,
323                    new OctetString("restrictedReadView"),
324                    new OctetString("restrictedWriteView"),
325                    new OctetString("restrictedNotifyView"),
326                    StorageType.nonVolatile);
327 
328     vacm.addViewTreeFamily(new OctetString("fullReadView"), new OID("1.3"),
329                            new OctetString(), VacmMIB.vacmViewIncluded,
330                            StorageType.nonVolatile);
331     vacm.addViewTreeFamily(new OctetString("fullWriteView"), new OID("1.3"),
332                            new OctetString(), VacmMIB.vacmViewIncluded,
333                            StorageType.nonVolatile);
334     vacm.addViewTreeFamily(new OctetString("fullNotifyView"), new OID("1.3"),
335                            new OctetString(), VacmMIB.vacmViewIncluded,
336                            StorageType.nonVolatile);
337 
338     vacm.addViewTreeFamily(new OctetString("restrictedReadView"),
339                            new OID("1.3.6.1.2"), new OctetString(),
340                            VacmMIB.vacmViewIncluded, StorageType.nonVolatile);
341     vacm.addViewTreeFamily(new OctetString("restrictedWriteView"),
342                            new OID("1.3.6.1.2.1"), new OctetString(),
343                            VacmMIB.vacmViewIncluded, StorageType.nonVolatile);
344     vacm.addViewTreeFamily(new OctetString("restrictedNotifyView"),
345                            new OID("1.3.6.1.2"), new OctetString(),
346                            VacmMIB.vacmViewIncluded, StorageType.nonVolatile);
347     vacm.addViewTreeFamily(new OctetString("restrictedNotifyView"),
348                            new OID("1.3.6.1.6.3.1"), new OctetString(),
349                            VacmMIB.vacmViewIncluded, StorageType.nonVolatile);
350   }
351 
352   /**
353    * The table of community strings configured in the SNMP engine's Local
354    * Configuration Datastore (LCD).
355    * <p>
356    * We only configure one, "public".
357    */
358   @Override
359   protected final void addCommunities(final SnmpCommunityMIB communityMIB) {
360     final Variable[] com2sec = {
361         new OctetString("public"), // community name
362         new OctetString("cpublic"), // security name
363         getAgent().getContextEngineID(), // local engine ID
364         new OctetString("public"), // default context name
365         new OctetString(), // transport tag
366         new Integer32(StorageType.nonVolatile), // storage type
367         new Integer32(RowStatus.active) // row status
368     };
369     final SnmpCommunityEntryRow row = communityMIB.getSnmpCommunityEntry()
370                                                   .createRow(new OctetString(
371                                                       "public2public").toSubIndex(
372                                                       true), com2sec);
373     communityMIB.getSnmpCommunityEntry().addRow(row);
374     if (isFilterAccessEnabled) {
375       snmpCommunityMIB.setSourceAddressFiltering(true);
376     }
377   }
378 
379   @Override
380   protected final void initTransportMappings() throws IOException {
381     final TransportMapping<?>[] testMappings =
382         new TransportMapping[address.length];
383     int nb = 0;
384     for (final String addres : address) {
385       final Address addr = GenericAddress.parse(addres);
386       if (addr != null) {
387         logger.info("SNMP Agent InitTransport: {} {}",
388                     addr.getClass().getSimpleName(), addr);
389         final TransportMapping<?> tm;
390         try {
391           tm = TransportMappings.getInstance().createTransportMapping(addr);
392         } catch (final RuntimeException e) {
393           continue;
394         }
395         if (tm != null) {
396           testMappings[nb] = tm;
397           nb++;
398         }
399       }
400     }
401     if (nb > 0) {
402       transportMappings = new TransportMapping<?>[nb];
403       System.arraycopy(testMappings, 0, transportMappings, 0, nb);
404     } else {
405       transportMappings = null;
406       throw new IOException("No address to connect");
407     }
408   }
409 
410   /**
411    * Start method invokes some initialization methods needed to start the
412    * agent
413    *
414    * @throws IOException
415    */
416   public final void start() throws IOException {
417     logger.debug("WaarpSnmpAgent starting: {} 1 on {}", address[0],
418                  address.length);
419     try {
420       init();
421     } catch (final IOException e) {
422       logger.warn("Error while SNMP starts " + " : {}", e.getMessage());
423       throw e;
424     }
425     addShutdownHook();
426     getServer().addContext(new OctetString("public"));
427     finishInit();
428     run();
429     if (TrapLevel.StartStop.isLevelValid(getTrapLevel())) {
430       sendColdStartNotification();
431     }
432   }
433 
434   @Override
435   protected final void sendColdStartNotification() {
436     logger.debug("ColdStartNotification: {}",
437                  getMib().getBaseOidStartOrShutdown());
438     final SNMPv2MIB snmpv2 = getMib().getSNMPv2MIB();
439     notificationOriginator.notify(new OctetString("public"),
440                                   SnmpConstants.coldStart,
441                                   new VariableBinding[] {
442                                       new VariableBinding(
443                                           getMib().getBaseOidStartOrShutdown(),
444                                           new OctetString("Startup Service")),
445                                       new VariableBinding(
446                                           SnmpConstants.sysDescr,
447                                           snmpv2.getDescr()),
448                                       new VariableBinding(
449                                           SnmpConstants.sysObjectID,
450                                           snmpv2.getObjectID()),
451                                       new VariableBinding(
452                                           SnmpConstants.sysContact,
453                                           snmpv2.getContact()),
454                                       new VariableBinding(SnmpConstants.sysName,
455                                                           snmpv2.getName()),
456                                       new VariableBinding(
457                                           SnmpConstants.sysLocation,
458                                           snmpv2.getLocation())
459                                   });
460   }
461 
462   /**
463    * Send a Notification just before Shutdown the SNMP service.
464    */
465   protected final void sendShutdownNotification() {
466     if (getMib() == null || notificationOriginator == null) {
467       return;
468     }
469     final SNMPv2MIB snmpv2 = getMib().getSNMPv2MIB();
470     notificationOriginator.notify(new OctetString("public"),
471                                   SnmpConstants.linkDown,
472                                   new VariableBinding[] {
473                                       new VariableBinding(
474                                           getMib().getBaseOidStartOrShutdown(),
475                                           new OctetString("Shutdown Service")),
476                                       new VariableBinding(
477                                           SnmpConstants.sysDescr,
478                                           snmpv2.getDescr()),
479                                       new VariableBinding(
480                                           SnmpConstants.sysObjectID,
481                                           snmpv2.getObjectID()),
482                                       new VariableBinding(
483                                           SnmpConstants.sysContact,
484                                           snmpv2.getContact()),
485                                       new VariableBinding(SnmpConstants.sysName,
486                                                           snmpv2.getName()),
487                                       new VariableBinding(
488                                           SnmpConstants.sysLocation,
489                                           snmpv2.getLocation())
490                                   });
491     try {
492       Thread.sleep(100);
493     } catch (final InterruptedException e) {//NOSONAR
494       SysErrLogger.FAKE_LOGGER.ignoreLog(e);
495     }
496   }
497 
498   @Override
499   public final void stop() {
500     logger.info("Stopping SNMP support");
501     if (TrapLevel.StartStop.isLevelValid(getTrapLevel())) {
502       sendShutdownNotification();
503     }
504     if (session != null) {
505       super.stop();
506     }
507     if (getMonitor() != null) {
508       getMonitor().releaseResources();
509       try {
510         Thread.sleep(100);
511       } catch (final InterruptedException e) {//NOSONAR
512         SysErrLogger.FAKE_LOGGER.ignoreLog(e);
513       }
514       if (workerPool != null) {
515         workerPool.cancel();
516       }
517     }
518   }
519 
520   /**
521    * @return the trapLevel
522    */
523   public final int getTrapLevel() {
524     return trapLevel;
525   }
526 
527   /**
528    * @param trapLevel the trapLevel to set
529    */
530   public final void setTrapLevel(final int trapLevel) {
531     this.trapLevel = trapLevel;
532   }
533 
534   /**
535    * @param monitor the monitor to set
536    */
537   private void setMonitor(final WaarpInterfaceMonitor monitor) {
538     this.monitor = monitor;
539   }
540 
541   /**
542    * @param mib the mib to set
543    */
544   private void setMib(final WaarpInterfaceMib mib) {
545     this.mib = mib;
546   }
547 }