View Javadoc

1   package net.sf.snmpadaptor4j.api.opennms;
2   
3   import java.net.InetAddress;
4   import java.util.Iterator;
5   import java.util.Map.Entry;
6   import net.sf.snmpadaptor4j.api.SnmpDaemon;
7   import net.sf.snmpadaptor4j.api.SnmpDaemonConfiguration;
8   import net.sf.snmpadaptor4j.api.SnmpException;
9   import net.sf.snmpadaptor4j.api.SnmpMib;
10  import net.sf.snmpadaptor4j.api.AttributeAccessor;
11  import net.sf.snmpadaptor4j.object.SnmpOid;
12  import org.apache.log4j.Level;
13  import org.opennms.protocols.snmp.SnmpAgentHandler;
14  import org.opennms.protocols.snmp.SnmpAgentSession;
15  import org.opennms.protocols.snmp.SnmpEndOfMibView;
16  import org.opennms.protocols.snmp.SnmpObjectId;
17  import org.opennms.protocols.snmp.SnmpOctetString;
18  import org.opennms.protocols.snmp.SnmpParameters;
19  import org.opennms.protocols.snmp.SnmpPduBulk;
20  import org.opennms.protocols.snmp.SnmpPduPacket;
21  import org.opennms.protocols.snmp.SnmpPduRequest;
22  import org.opennms.protocols.snmp.SnmpPeer;
23  import org.opennms.protocols.snmp.SnmpSMI;
24  import org.opennms.protocols.snmp.SnmpSyntax;
25  import org.opennms.protocols.snmp.SnmpVarBind;
26  import org.opennms.protocols.snmp.asn1.AsnEncodingException;
27  
28  /**
29   * SNMP daemon implementation for <b>joesnmp</b> API.
30   * @author <a href="http://fr.linkedin.com/in/jpminetti/">Jean-Philippe MINETTI</a>
31   */
32  public class OpennmsSnmpDaemon
33  		extends OpennmsSupport
34  		implements SnmpDaemon, SnmpAgentHandler {
35  
36  	/**
37  	 * Configuration settings of SNMP daemon.
38  	 */
39  	private final SnmpDaemonConfiguration configuration;
40  
41  	/**
42  	 * SNMP <b>M</b>anagement <b>I</b>nformation <b>B</b>ase (MIB).
43  	 */
44  	protected final SnmpMib snmpMib;
45  
46  	/**
47  	 * SNMP session.
48  	 */
49  	private SnmpAgentSession snmpSession = null;
50  
51  	/**
52  	 * Hidden constructor.
53  	 * @param configuration Configuration settings of SNMP daemon.
54  	 * @param snmpMib SNMP <b>M</b>anagement <b>I</b>nformation <b>B</b>ase (MIB).
55  	 * @see OpennmsSnmpApiFactory#newSnmpDaemon(SnmpDaemonConfiguration, SnmpMib)
56  	 */
57  	OpennmsSnmpDaemon (final SnmpDaemonConfiguration configuration, final SnmpMib snmpMib) {
58  		super();
59  		this.configuration = configuration;
60  		this.snmpMib = snmpMib;
61  	}
62  
63  	/*
64  	 * {@inheritDoc}
65  	 * @see net.sf.snmpadaptor4j.api.SnmpDaemon#start()
66  	 */
67  	public final synchronized void start () throws Exception {
68  		if (this.snmpSession == null) {
69  			this.logger.trace("SNMP daemon starting...");
70  			this.logger.debug("SNMP daemon will listen on " + this.configuration.getListenerAddress() + ":" + this.configuration.getListenerPort().intValue());
71  			final SnmpPeer peer = new SnmpPeer(InetAddress.getByName(this.configuration.getListenerAddress()), this.configuration.getListenerPort().intValue());
72  			final SnmpParameters params = peer.getParameters();
73  			switch (this.configuration.getListenerSnmpVersion().intValue()) {
74  				case 1:
75  					params.setVersion(SnmpSMI.SNMPV1);
76  					break;
77  				default:
78  					params.setVersion(SnmpSMI.SNMPV2);
79  					break;
80  			}
81  			params.setReadCommunity(this.configuration.getListenerReadCommunity());
82  			params.setWriteCommunity(this.configuration.getListenerWriteCommunity());
83  			this.snmpSession = new SnmpAgentSession(this, peer);
84  			this.logger.info("SNMP daemon listen on " + this.configuration.getListenerAddress() + ":" + this.configuration.getListenerPort().intValue());
85  			this.logger.trace("SNMP daemon started");
86  		}
87  		else {
88  			this.logger.trace("SNMP daemon already started");
89  		}
90  	}
91  
92  	/*
93  	 * {@inheritDoc}
94  	 * @see net.sf.snmpadaptor4j.api.SnmpDaemon#stop()
95  	 */
96  	public final synchronized void stop () {
97  		if (this.snmpSession != null) {
98  			this.logger.trace("SNMP daemon stopping...");
99  			this.snmpSession.close();
100 			this.snmpSession = null;
101 			this.logger.trace("SNMP daemon stopped");
102 		}
103 		else {
104 			this.logger.trace("SNMP daemon already stopped");
105 		}
106 	}
107 
108 	/*
109 	 * {@inheritDoc}
110 	 * @see net.sf.snmpadaptor4j.api.SnmpDaemon#isStarted()
111 	 */
112 	public final boolean isStarted () {
113 		return (this.snmpSession != null);
114 	}
115 
116 	/*
117 	 * {@inheritDoc}
118 	 * @see org.opennms.protocols.snmp.SnmpAgentHandler#SnmpAgentSessionError(org.opennms.protocols.snmp.SnmpAgentSession, int, java.lang.Object)
119 	 */
120 	public final void SnmpAgentSessionError (final SnmpAgentSession session, final int error, final Object ref) {
121 		throw new SnmpException(error);
122 	}
123 
124 	/*
125 	 * {@inheritDoc}
126 	 * @see org.opennms.protocols.snmp.SnmpAgentHandler#snmpReceivedPdu(org.opennms.protocols.snmp.SnmpAgentSession, java.net.InetAddress, int,
127 	 * org.opennms.protocols.snmp.SnmpOctetString, org.opennms.protocols.snmp.SnmpPduPacket)
128 	 */
129 	public final void snmpReceivedPdu (final SnmpAgentSession session, final InetAddress manager, final int port, final SnmpOctetString community,
130 			final SnmpPduPacket pdu) {
131 		if (this.logger.isTraceEnabled()) {
132 			this.logger.trace(pdu.getRequestId() + ": in treating by snmpReceivedPdu...");
133 		}
134 		if (pdu instanceof SnmpPduBulk) {
135 			final SnmpPduRequest response = new SnmpPduRequest(SnmpPduPacket.RESPONSE);
136 			response.setRequestId(pdu.getRequestId());
137 			doBulk(pdu.toVarBindArray(), (SnmpPduBulk) pdu, response);
138 			try {
139 				sendPduPacket(session, new SnmpPeer(manager, port), response);
140 			}
141 			catch (final AsnEncodingException e) {
142 				this.logger.error(pdu.getRequestId() + ": error " + SnmpPduPacket.ErrTooBig + " (response too big)");
143 				final SnmpPduRequest errorResponse = new SnmpPduRequest(SnmpPduPacket.RESPONSE);
144 				errorResponse.setRequestId(pdu.getRequestId());
145 				errorResponse.setErrorStatus(SnmpPduPacket.ErrTooBig);
146 				errorResponse.setErrorIndex(0);
147 				try {
148 					sendPduPacket(session, new SnmpPeer(manager, port), errorResponse);
149 				}
150 				catch (final Throwable e1) {
151 					this.logger.error(pdu.getRequestId() + ": an error has occurred when the error response sending", e1);
152 				}
153 			}
154 			catch (final Throwable e) {
155 				this.logger.error(pdu.getRequestId() + ": error " + SnmpPduPacket.ErrGenError + " (general error)", e);
156 				final SnmpPduRequest errorResponse = new SnmpPduRequest(SnmpPduPacket.RESPONSE);
157 				errorResponse.setRequestId(pdu.getRequestId());
158 				errorResponse.setErrorStatus(SnmpPduPacket.ErrGenError);
159 				errorResponse.setErrorIndex(0);
160 				try {
161 					sendPduPacket(session, new SnmpPeer(manager, port), errorResponse);
162 				}
163 				catch (final Throwable e1) {
164 					this.logger.error(pdu.getRequestId() + ": an error has occurred when the error response sending", e1);
165 				}
166 			}
167 		}
168 		else {
169 			this.logger.error(pdu.getRequestId() + ": request ignored (PDU " + pdu.getClass().getSimpleName() + " unhandled)");
170 		}
171 		if (this.logger.isTraceEnabled()) {
172 			this.logger.trace(pdu.getRequestId() + ": treated by snmpReceivedPdu");
173 		}
174 	}
175 
176 	/**
177 	 * Sends a {@link SnmpPduPacket}.
178 	 * @param session SNMP session.
179 	 * @param peer Request destination.
180 	 * @param pdu Request.
181 	 * @throws Exception Exception if an error has occurred.
182 	 */
183 	@SuppressWarnings("static-method")
184 	protected void sendPduPacket (final SnmpAgentSession session, final SnmpPeer peer, final SnmpPduPacket pdu) throws Exception {
185 		session.send(peer, pdu);
186 	}
187 
188 	/*
189 	 * {@inheritDoc}
190 	 * @see org.opennms.protocols.snmp.SnmpAgentHandler#snmpReceivedGet(org.opennms.protocols.snmp.SnmpPduPacket, boolean)
191 	 */
192 	public final SnmpPduRequest snmpReceivedGet (final SnmpPduPacket pdu, final boolean getNext) {
193 		if (this.logger.isTraceEnabled()) {
194 			this.logger.trace(pdu.getRequestId() + ": in treating by snmpReceivedGet...");
195 		}
196 		final SnmpPduRequest response = new SnmpPduRequest(SnmpPduPacket.RESPONSE);
197 		response.setRequestId(pdu.getRequestId());
198 		final SnmpVarBind[] binds = pdu.toVarBindArray();
199 		if (binds.length > 20) {
200 			this.logger.error(pdu.getRequestId() + ": error " + SnmpPduPacket.ErrTooBig + " (too OIDs - limited to 20)");
201 			response.setErrorStatus(SnmpPduPacket.ErrTooBig);
202 			response.setErrorIndex(0);
203 		}
204 
205 		// SNMP v1
206 		else if (pdu instanceof SnmpPduRequest) {
207 			int resultIndex = 1;
208 			try {
209 				SnmpOid oid;
210 				AttributeAccessor attributeAccessor;
211 				for (final SnmpVarBind varBind : binds) {
212 					oid = SnmpOid.newInstance(varBind.getName().getIdentifiers());
213 					if (getNext) {
214 						attributeAccessor = this.snmpMib.next(oid);
215 						if (attributeAccessor == null) {
216 							response.addVarBind(new SnmpVarBind(varBind.getName()));
217 							throw new SnmpException("none OID found after " + oid, SnmpPduPacket.ErrNoSuchName, Level.DEBUG);
218 						}
219 						if (this.logger.isDebugEnabled()) {
220 							this.logger.debug(pdu.getRequestId() + ": " + oid + " --next--> " + attributeAccessor);
221 						}
222 						response.addVarBind(newSnmpVarBind(attributeAccessor));
223 					}
224 					else {
225 						attributeAccessor = this.snmpMib.find(oid);
226 						if (attributeAccessor == null) {
227 							response.addVarBind(new SnmpVarBind(varBind.getName()));
228 							throw new SnmpException("OID " + oid + " not found", SnmpPduPacket.ErrNoSuchName, Level.DEBUG);
229 						}
230 						if (this.logger.isDebugEnabled()) {
231 							this.logger.debug(pdu.getRequestId() + ": " + attributeAccessor);
232 						}
233 						varBind.setValue(newSnmpValue(attributeAccessor));
234 						response.addVarBind(varBind);
235 					}
236 					resultIndex++;
237 				}
238 			}
239 			catch (final SnmpException e) {
240 				this.logger.log(e.getLoggerLevel(), pdu.getRequestId() + ": " + e.getMessage(), e.getCause());
241 				response.setErrorStatus(e.getErrorStatus());
242 				response.setErrorIndex(resultIndex);
243 			}
244 			catch (final Throwable e) {
245 				this.logger.error(pdu.getRequestId() + ": error " + SnmpPduPacket.ErrGenError + " (general error)", e);
246 				response.setErrorStatus(SnmpPduPacket.ErrGenError);
247 				response.setErrorIndex(resultIndex);
248 			}
249 		}
250 
251 		// SNMP v2
252 		else if (pdu instanceof SnmpPduBulk) {
253 			doBulk(binds, (SnmpPduBulk) pdu, response);
254 		}
255 
256 		else {
257 			this.logger.error(pdu.getRequestId() + ": error " + SnmpPduPacket.ErrGenError + " (PDU " + pdu.getClass().getSimpleName() + " unhandled)");
258 			response.setErrorStatus(SnmpPduPacket.ErrGenError);
259 			response.setErrorIndex(0);
260 		}
261 		if (this.logger.isTraceEnabled()) {
262 			this.logger.trace(pdu.getRequestId() + ": treated by snmpReceivedGet");
263 		}
264 		return response;
265 	}
266 
267 	/**
268 	 * Treats a <code>get-bulk</code> operation (SNMP v2).
269 	 * @param binds OID list to apply a <code>get-bulk</code>.
270 	 * @param bulk <code>get-bulk</code> operation.
271 	 * @param response Response to the request.
272 	 */
273 	private void doBulk (final SnmpVarBind binds[], final SnmpPduBulk bulk, final SnmpPduRequest response) {
274 		int resultIndex = 1;
275 		try {
276 			SnmpOid oid;
277 			AttributeAccessor attributeAccessor;
278 			Iterator<Entry<SnmpOid, AttributeAccessor>> attributeAccessorEntryIterator;
279 			int i;
280 			for (final SnmpVarBind varBind : binds) {
281 				oid = SnmpOid.newInstance(varBind.getName().getIdentifiers());
282 				if (resultIndex <= bulk.getNonRepeaters()) {
283 					attributeAccessor = this.snmpMib.next(oid);
284 					if (attributeAccessor != null) {
285 						if (this.logger.isDebugEnabled()) {
286 							this.logger.debug(bulk.getRequestId() + ": " + oid + " --next--> " + attributeAccessor);
287 						}
288 						response.addVarBind(newSnmpVarBind(attributeAccessor));
289 					}
290 					else {
291 						if (this.logger.isDebugEnabled()) {
292 							this.logger.debug(bulk.getRequestId() + ": " + oid + " --next--> END OF MIB");
293 						}
294 						response.addVarBind(new SnmpVarBind(varBind.getName(), new SnmpEndOfMibView()));
295 					}
296 				}
297 				else {
298 					attributeAccessorEntryIterator = this.snmpMib.nextSet(oid).entrySet().iterator();
299 					if (attributeAccessorEntryIterator.hasNext()) {
300 						i = 0;
301 						while (attributeAccessorEntryIterator.hasNext() && (i < bulk.getMaxRepititions())) {
302 							attributeAccessor = attributeAccessorEntryIterator.next().getValue();
303 							if (this.logger.isDebugEnabled()) {
304 								if (i == 0) {
305 									this.logger.debug(bulk.getRequestId() + ": " + oid + " --next--> " + attributeAccessor);
306 								}
307 								else {
308 									this.logger.debug(bulk.getRequestId() + ": " + String.format("%" + oid.toString().length() + "s", "") + " --next--> "
309 											+ attributeAccessor);
310 								}
311 							}
312 							response.addVarBind(newSnmpVarBind(attributeAccessor));
313 							i++;
314 						}
315 						if (i < bulk.getMaxRepititions()) {
316 							if (this.logger.isDebugEnabled()) {
317 								this.logger.debug(bulk.getRequestId() + ": " + oid + " --next--> .1.9: END OF MIB");
318 							}
319 							response.addVarBind(new SnmpVarBind(".1.9", new SnmpEndOfMibView()));
320 						}
321 					}
322 					else {
323 						if (this.logger.isDebugEnabled()) {
324 							this.logger.debug(bulk.getRequestId() + ": " + oid + " --next--> END OF MIB");
325 						}
326 						response.addVarBind(new SnmpVarBind(varBind.getName(), new SnmpEndOfMibView()));
327 					}
328 				}
329 				resultIndex++;
330 			}
331 		}
332 		catch (final SnmpException e) {
333 			this.logger.log(e.getLoggerLevel(), bulk.getRequestId() + ": " + e.getMessage(), e.getCause());
334 			response.setErrorStatus(e.getErrorStatus());
335 			response.setErrorIndex(resultIndex);
336 		}
337 		catch (final Throwable e) {
338 			this.logger.error(bulk.getRequestId() + ": error " + SnmpPduPacket.ErrGenError + " (general error)", e);
339 			response.setErrorStatus(SnmpPduPacket.ErrGenError);
340 			response.setErrorIndex(resultIndex);
341 		}
342 	}
343 
344 	/*
345 	 * {@inheritDoc}
346 	 * @see org.opennms.protocols.snmp.SnmpAgentHandler#snmpReceivedSet(org.opennms.protocols.snmp.SnmpPduPacket)
347 	 */
348 	public SnmpPduRequest snmpReceivedSet (final SnmpPduPacket pdu) {
349 		if (this.logger.isTraceEnabled()) {
350 			this.logger.trace(pdu.getRequestId() + ": in treating by snmpReceivedSet...");
351 		}
352 		final SnmpPduRequest response = new SnmpPduRequest(SnmpPduPacket.RESPONSE);
353 		response.setRequestId(pdu.getRequestId());
354 		final SnmpVarBind[] binds = pdu.toVarBindArray();
355 		if (binds.length > 20) {
356 			this.logger.error(pdu.getRequestId() + ": error " + SnmpPduPacket.ErrTooBig + " (too OIDs - limited to 20)");
357 			response.setErrorStatus(SnmpPduPacket.ErrTooBig);
358 			response.setErrorIndex(0);
359 		}
360 		else {
361 			int resultIndex = 1;
362 			try {
363 				SnmpOid oid;
364 				SnmpSyntax newValue;
365 				AttributeAccessor attributeAccessor;
366 				for (final SnmpVarBind varBind : binds) {
367 					oid = SnmpOid.newInstance(varBind.getName().getIdentifiers());
368 					newValue = varBind.getValue();
369 					attributeAccessor = this.snmpMib.find(oid);
370 					if (attributeAccessor == null) {
371 						response.addVarBind(new SnmpVarBind(varBind.getName()));
372 						throw new SnmpException("OID " + oid + " not found", SnmpPduPacket.ErrNoSuchName, Level.DEBUG);
373 					}
374 					if (this.logger.isDebugEnabled()) {
375 						this.logger.debug(pdu.getRequestId() + ": old " + attributeAccessor);
376 					}
377 					setValue(attributeAccessor, newValue);
378 					response.addVarBind(newSnmpVarBind(attributeAccessor));
379 					if (this.logger.isDebugEnabled()) {
380 						this.logger.debug(pdu.getRequestId() + ": new " + attributeAccessor);
381 					}
382 					resultIndex++;
383 				}
384 			}
385 			catch (final SnmpException e) {
386 				this.logger.log(e.getLoggerLevel(), pdu.getRequestId() + ": " + e.getMessage(), e.getCause());
387 				response.setErrorStatus(e.getErrorStatus());
388 				response.setErrorIndex(resultIndex);
389 			}
390 			catch (final Throwable e) {
391 				this.logger.error(pdu.getRequestId() + ": error " + SnmpPduPacket.ErrGenError + " (general error)", e);
392 				response.setErrorStatus(SnmpPduPacket.ErrGenError);
393 				response.setErrorIndex(resultIndex);
394 			}
395 		}
396 		if (this.logger.isTraceEnabled()) {
397 			this.logger.trace(pdu.getRequestId() + ": treated by snmpReceivedSet");
398 		}
399 		return response;
400 	}
401 
402 	/**
403 	 * Creates and returns a new {@link SnmpVarBind} by a {@link AttributeAccessor}.
404 	 * @param attributeAccessor {@link AttributeAccessor} containing all the necessary data for create the {@link SnmpVarBind}.
405 	 * @return New {@link SnmpVarBind}.
406 	 * @throws Exception Exception if an error has occurred.
407 	 */
408 	private SnmpVarBind newSnmpVarBind (final AttributeAccessor attributeAccessor) throws Exception {
409 		return new SnmpVarBind(new SnmpObjectId(attributeAccessor.getOid().getOid()), newSnmpValue(attributeAccessor));
410 	}
411 
412 	/**
413 	 * Creates and returns a new {@link SnmpSyntax} by a {@link AttributeAccessor}.
414 	 * @param attributeAccessor {@link AttributeAccessor} containing all the necessary data for create the {@link SnmpSyntax}.
415 	 * @return New {@link SnmpSyntax}.
416 	 * @throws Exception Exception if an error has occurred.
417 	 */
418 	protected static SnmpSyntax newSnmpValue (final AttributeAccessor attributeAccessor) throws Exception {
419 		if (!attributeAccessor.isReadable()) {
420 			throw new SnmpException("MBean attribute is not readable", SnmpPduPacket.ErrNoAccess);
421 		}
422 		Object value;
423 		try {
424 			value = attributeAccessor.getValue();
425 		}
426 		catch (final Throwable e) {
427 			throw new SnmpException("Impossible to read the attribute due to error", SnmpPduPacket.ErrNoAccess, e);
428 		}
429 		return newSnmpValue(attributeAccessor.getSnmpDataType(), value);
430 	}
431 
432 	/**
433 	 * Sets a new value to {@link AttributeAccessor}.
434 	 * @param attributeAccessor {@link AttributeAccessor} to update.
435 	 * @param newValue New value encapsulated to an object {@link SnmpSyntax}.
436 	 * @throws Exception Exception if an error has occurred.
437 	 */
438 	protected static void setValue (final AttributeAccessor attributeAccessor, final SnmpSyntax newValue) throws Exception {
439 		if (!attributeAccessor.isReadable()) {
440 			throw new SnmpException("MBean attribute is not readable", SnmpPduPacket.ErrNoAccess);
441 		}
442 		if (!attributeAccessor.isWritable()) {
443 			throw new SnmpException("MBean attribute is not writable", SnmpPduPacket.ErrNotWritable);
444 		}
445 		final Object value = newJmxValue(attributeAccessor.getJmxDataType(), attributeAccessor.getSnmpDataType(), newValue);
446 		try {
447 			attributeAccessor.setValue(value);
448 		}
449 		catch (final Throwable e) {
450 			throw new SnmpException("Impossible to write in the attribute due to error", SnmpPduPacket.ErrNotWritable, e);
451 		}
452 	}
453 
454 	/*
455 	 * {@inheritDoc}
456 	 * @see java.lang.Object#toString()
457 	 */
458 	@Override
459 	public String toString () {
460 		return "SnmpDaemon:opennms[" + (this.configuration != null ? this.configuration.getListenerAddress() + ":" + this.configuration.getListenerPort() : "null")
461 				+ "]";
462 	}
463 
464 }