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
30
31
32 public class OpennmsSnmpDaemon
33 extends OpennmsSupport
34 implements SnmpDaemon, SnmpAgentHandler {
35
36
37
38
39 private final SnmpDaemonConfiguration configuration;
40
41
42
43
44 protected final SnmpMib snmpMib;
45
46
47
48
49 private SnmpAgentSession snmpSession = null;
50
51
52
53
54
55
56
57 OpennmsSnmpDaemon (final SnmpDaemonConfiguration configuration, final SnmpMib snmpMib) {
58 super();
59 this.configuration = configuration;
60 this.snmpMib = snmpMib;
61 }
62
63
64
65
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
94
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
110
111
112 public final boolean isStarted () {
113 return (this.snmpSession != null);
114 }
115
116
117
118
119
120 public final void SnmpAgentSessionError (final SnmpAgentSession session, final int error, final Object ref) {
121 throw new SnmpException(error);
122 }
123
124
125
126
127
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
178
179
180
181
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
190
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
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
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
269
270
271
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
346
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
404
405
406
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
414
415
416
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
434
435
436
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
456
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 }