package jnetman.snmp; import java.io.IOException; import java.util.Vector; import jnetman.network.AddressException; import jnetman.network.NetworkDevice; import jnetman.session.SnmpPref; import org.apache.log4j.Logger; import org.snmp4j.PDU; import org.snmp4j.ScopedPDU; import org.snmp4j.Snmp; import org.snmp4j.Target; import org.snmp4j.UserTarget; import org.snmp4j.event.ResponseEvent; import org.snmp4j.log.Log4jLogFactory; import org.snmp4j.log.LogFactory; import org.snmp4j.mp.MPv3; import org.snmp4j.mp.SnmpConstants; import org.snmp4j.security.AuthMD5; import org.snmp4j.security.PrivDES; import org.snmp4j.security.SecurityLevel; import org.snmp4j.security.SecurityModels; import org.snmp4j.security.SecurityProtocols; import org.snmp4j.security.USM; import org.snmp4j.security.UsmUser; import org.snmp4j.smi.Address; import org.snmp4j.smi.GenericAddress; import org.snmp4j.smi.Integer32; import org.snmp4j.smi.OID; import org.snmp4j.smi.OctetString; import org.snmp4j.smi.Variable; import org.snmp4j.smi.VariableBinding; import org.snmp4j.transport.DefaultUdpTransportMapping; import org.snmp4j.util.PDUFactory; import org.snmp4j.util.TreeEvent; import org.snmp4j.util.TreeListener; import org.snmp4j.util.TreeUtils; public class SnmpClient implements PDUFactory { private Snmp snmpInstance; private NetworkDevice targetDevice; private static int requestID = 1; private Logger logger; /** * Create a new SnmpClient for the passed NetworkDevice. * * @param targetDevice * NetworkDevice that will be used for SNMP message exchange. */ public SnmpClient(NetworkDevice targetDevice) { this.targetDevice = targetDevice; // Create a new logger with name jnetman.snmp.device_name logger = Logger.getLogger("snmp.snmpClient." + targetDevice.getName()); // If activated SNMP4J will show a huge amount of low level debug info if (SnmpPref.isSnmp4jLogEnabled()) LogFactory.setLogFactory(new Log4jLogFactory()); try { DefaultUdpTransportMapping transport = new DefaultUdpTransportMapping(); // Creates Snmp istance for that transport channel snmpInstance = new Snmp(transport); logger.debug("New SNMP Client crated"); // Creates v3 SNMP USM USM usm = new USM(SecurityProtocols.getInstance(), new OctetString( MPv3.createLocalEngineID()), 0); SecurityModels.getInstance().addSecurityModel(usm); // Adds 'jnetman' usm using MD5 authentication and DES encryption UsmUser jnetmanUser = new UsmUser( new OctetString(SnmpPref.getUser()), AuthMD5.ID, new OctetString(SnmpPref.getPassword()), PrivDES.ID, new OctetString(SnmpPref.getPassword())); snmpInstance.getUSM().addUser(new OctetString(SnmpPref.getUser()), jnetmanUser); logger.debug("New USM User added >> " + jnetmanUser.getSecurityName()); // Enables listening for incoming SNMP packet transport.listen(); } catch (IOException e) { logger.fatal( "IOException while creating a new DefaultUdpTransportMapping()", e); System.exit(-1); } } public NetworkDevice getNetworkDevice() { return this.targetDevice; } /** * Returns a destination target to be used for a new SNMP message. The * target returned will use SNMP v3 protocol with authentication and privacy * enabled. * * @return v3 authPriv Target * @throws AddressException */ private Target getV3AuthPrivTarget() { String hostAddress = null; try { hostAddress = targetDevice.getAddress().getHostAddress(); } catch (AddressException e) { logger.fatal("Unable to get the IP address from the NetworkDevice", e); System.exit(-1); } // set the address using the format 'udp:192.168.1.1/161' String address = String.format("udp:%s/%d", hostAddress, SnmpPref.getPort()); Address targetAddress = GenericAddress.parse(address); UserTarget target = new UserTarget(); target.setAddress(targetAddress); target.setRetries(SnmpPref.getMaxRetries()); target.setTimeout(SnmpPref.getTimeout()); target.setVersion(SnmpConstants.version3); target.setSecurityLevel(SecurityLevel.AUTH_PRIV); target.setSecurityName(new OctetString(SnmpPref.getUser())); return target; } /** * Send a new GET request for multiple OIDs. * * @param oids * Array of OID for the request. * @return ResponseEvent for the request only if no timeout has occurred. * @throws TimeoutException * If request has timed out */ public VariableBinding[] get(OID oids[]) throws TimeoutException, SnmpErrorException { return this.packPDUAndSend(oids, PDU.GET); } /** * Send a new GET request for a single OID. * * @param oid * OID of the request. * @return ResponseEvent for the request only if no timeout has occurred. * @throws TimeoutException * If request has timed out */ public VariableBinding get(OID oid) throws TimeoutException, SnmpErrorException { return this.packPDUAndSend(new OID[] { new OID(oid) }, PDU.GET)[0]; } /** * Send a new GETNEXT request for multiple OIDs. * * @param oids * Array of OID for the request. * @return ResponseEvent for the request only if no timeout has occurred. * @throws TimeoutException * If request has timed out */ public VariableBinding[] getNext(OID oids[]) throws TimeoutException, SnmpErrorException { return this.packPDUAndSend(oids, PDU.GETNEXT); } /** * Send a new GETNEXT request for a single OID * * @param oid * OID of the request * @return ResponseEvent for the request only if no timeout has occurred * @throws TimeoutException * If request has timed out */ public VariableBinding getNext(OID oid) throws TimeoutException, SnmpErrorException { return this.packPDUAndSend(new OID[] { new OID(oid) }, PDU.GETNEXT)[0]; } /** * Send a new SET request for a single OID/value pair * * @param oid * OID of the value to write * @param value * New value to write * @return ResponseEvent for the request only if no timeout has occurred * @throws TimeoutException * If request has timed out */ public VariableBinding set(OID oid, Variable value) throws TimeoutException, SnmpErrorException { return this.packPDUAndSend(new VariableBinding[] { new VariableBinding( oid, value) }, PDU.SET)[0]; } /** * Send a new SET request for a single VariableBinding * * @param vb * VariableBinding of the request * @return ResponseEvent for the request only if no timeout has occurred * @throws TimeoutException * If request has timed out */ public VariableBinding set(VariableBinding vb) throws TimeoutException, SnmpErrorException { return this.packPDUAndSend(new VariableBinding[] { vb }, PDU.SET)[0]; } /** * Send a new SET request for multiple VariableBindings * * @param vb * Array of VariableBinding of the request * @return ResponseEvent for the request only if no timeout has occurred * @throws TimeoutException * If request has timed out */ public VariableBinding[] set(VariableBinding[] vbs) throws TimeoutException, SnmpErrorException { return this.packPDUAndSend(vbs, PDU.SET); } public VariableBinding[] walk(OID oid) { logger.debug("Starting walk at OID " + oid.toString() + "..."); final Vector<VariableBinding> snapshot = new Vector<VariableBinding>(); final WalkCounts counts = new WalkCounts(); final long startTime = System.nanoTime(); TreeUtils treeUtils = new TreeUtils(snmpInstance, this); TreeListener treeListener = new TreeListener() { private boolean finished; public boolean next(TreeEvent e) { counts.requests++; if (e.getVariableBindings() != null) { VariableBinding[] vbs = e.getVariableBindings(); counts.objects += vbs.length; for (VariableBinding vb : vbs) snapshot.add(vb); } return true; } public void finished(TreeEvent e) { if ((e.getVariableBindings() != null) && (e.getVariableBindings().length > 0)) next(e); logger.debug("Walk completed in " + (System.nanoTime() - startTime) / 1000000 + " ms, " + counts.objects + " objects received in " + counts.requests + " requests"); if (e.isError()) logger.debug("The following error occurred during walk: " + e.getErrorMessage()); finished = true; synchronized (this) { this.notify(); } } public boolean isFinished() { return finished; } }; synchronized (treeListener) { treeUtils .getSubtree(getV3AuthPrivTarget(), oid, null, treeListener); try { treeListener.wait(); } catch (InterruptedException ex) { logger.debug("Tree retrieval interrupted: " + ex.getMessage()); Thread.currentThread().interrupt(); } } return snapshot.toArray(new VariableBinding[snapshot.size()]); } public VariableBinding[] packPDUAndSend(OID[] oids, int pduType) throws TimeoutException, SnmpErrorException { PDU pdu = new ScopedPDU(); for (OID oid : oids) pdu.add(new VariableBinding(oid)); pdu.setType(pduType); return this.send(pdu); } public VariableBinding[] packPDUAndSend(VariableBinding[] vbs, int pduType) throws TimeoutException, SnmpErrorException { PDU pdu = new ScopedPDU(); pdu.addAll(vbs); pdu.setType(pduType); return this.send(pdu); } private synchronized VariableBinding[] send(PDU pdu) throws TimeoutException, SnmpErrorException { ResponseEvent event = null; /* * Well, it's now time to send a new SNMP message! Set the RequestID of * the PDU and increment the counter for the next operation. */ pdu.setRequestID(new Integer32(requestID)); requestID++; /* * The PDU is now ready to be sent */ Target target = getV3AuthPrivTarget(); // Target target = getV3AuthNoPrivTarget(); /* * Some debug about the request that is going to be sent. */ logger.debug("Sending request to " + target.getAddress() + " >> " + pdu); /* * Calculate the time elapsed between the beginning of request * transmission and the end of the response reception, for debug * purpose.s */ long startTime = System.nanoTime(); try { event = snmpInstance.send(pdu, target, null); } catch (IOException e) { logger.fatal("IOException while sending a new SNMP message", e); System.exit(-1); } long timeElapsed = System.nanoTime() - startTime; /* * If response != null means that no timeout has occurred and a response * was successfully delivered. */ if (event.getResponse() != null) { PDU response = event.getResponse(); /* * Some debug about the response received */ logger.debug("Response received from " + event.getPeerAddress() + " in " + timeElapsed / 1000000 + " ms >> " + response); /* * Check for common SNMP errors due to failed request */ if (response.getErrorStatus() == PDU.noError) { /* * Let's finally return the event, this could still contain a * SnmpSyntaxException (noSuchInstance, noSuchObject or * endOfMibView) but we don't care throwing it now. The client * user could be interested in doing it. */ return response.toArray(); } else { /* * SNMP error has occurred. */ /* * TODO Net-Snmp REPORT for failed authentication */ logger.error("SNMP ERROR >> " + response.getErrorStatusText()); throw new SnmpErrorException(response.getErrorStatusText()); } } /* * If here means that response == null, REQUEST TIME OUT! */ logger.error("Request TIMEOUT! No response received from " + target.getAddress()); throw new TimeoutException(target.getAddress()); } public PDU createPDU(Target target) { return new ScopedPDU(); } private class WalkCounts { public int requests; public int objects; } }