SNMP.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. <?php
  2. /*
  3. Copyright (c) 2012, Open Source Solutions Limited, Dublin, Ireland
  4. All rights reserved.
  5. Contact: Barry O'Donovan - barry (at) opensolutions (dot) ie
  6. http://www.opensolutions.ie/
  7. This file is part of the OSS_SNMP package.
  8. Redistribution and use in source and binary forms, with or without
  9. modification, are permitted provided that the following conditions are met:
  10. * Redistributions of source code must retain the above copyright
  11. notice, this list of conditions and the following disclaimer.
  12. * Redistributions in binary form must reproduce the above copyright
  13. notice, this list of conditions and the following disclaimer in the
  14. documentation and/or other materials provided with the distribution.
  15. * Neither the name of Open Source Solutions Limited nor the
  16. names of its contributors may be used to endorse or promote products
  17. derived from this software without specific prior written permission.
  18. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
  22. DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. */
  29. namespace OSS_SNMP;
  30. //// Add a trivial auto-loader
  31. //spl_autoload_register( function( $class ) {
  32. // if( substr( $class, 0, 9 ) == 'OSS_SNMP\\' )
  33. // {
  34. // $class = str_replace( '\\', '/', $class );
  35. // require( dirname( __FILE__ ) . '/../' . $class . '.php' );
  36. // }
  37. //});
  38. /**
  39. * A class for performing SNMP V2 queries and processing the results.
  40. *
  41. * @copyright Copyright (c) 2012, Open Source Solutions Limited, Dublin, Ireland
  42. * @author Barry O'Donovan <barry@opensolutions.ie>
  43. */
  44. class SNMP
  45. {
  46. /**
  47. * The SNMP community to use when polling SNMP services. Defaults to 'public' by the constructor.
  48. *
  49. * @var string The SNMP community to use when polling SNMP services. Defaults to 'public' by the constructor.
  50. */
  51. protected $_community;
  52. /**
  53. * The SNMP host to query. Defaults to '127.0.0.1'
  54. * @var string The SNMP host to query. Defaults to '127.0.0.1' by the constructor.
  55. */
  56. protected $_host;
  57. /**
  58. * The SNMP query timeout value (microseconds). Default: 1000000
  59. * @var int The SNMP query timeout value (microseconds). Default: 1000000
  60. */
  61. protected $_timeout = 1000000;
  62. /**
  63. * The SNMP query retry count. Default: 5
  64. * @var int The SNMP query retry count. Default: 5
  65. */
  66. protected $_retry = 5;
  67. /**
  68. * A variable to hold the last unaltered result of an SNMP query
  69. * @var mixed The last unaltered result of an SNMP query
  70. */
  71. protected $_lastResult = null;
  72. /**
  73. * The cache object to use as the cache
  74. * @var \OSS_SNMP\Cache The cache object to use
  75. */
  76. protected $_cache = null;
  77. /**
  78. * Set to true to disable local cache lookup and force SNMP queries
  79. *
  80. * Results are still stored. If you need to force a SNMP query, you can:
  81. *
  82. * $snmp = new OSS_SNMP( ... )'
  83. * ...
  84. * $snmp->disableCache();
  85. * $snmp->get( ... );
  86. * $snmp->enableCache();
  87. */
  88. protected $_disableCache = false;
  89. /**
  90. * SNMP output constants to mirror those of PHP
  91. * @var SNMP output constants to mirror those of PHP
  92. */
  93. const OID_OUTPUT_FULL = SNMP_OID_OUTPUT_FULL;
  94. /**
  95. * SNMP output constants to mirror those of PHP
  96. * @var SNMP output constants to mirror those of PHP
  97. */
  98. const OID_OUTPUT_NUMERIC = SNMP_OID_OUTPUT_NUMERIC;
  99. /**
  100. * Definition of an SNMP return type 'TruthValue'
  101. * @var Definition of an SNMP return type 'TruthValue'
  102. */
  103. const SNMP_TRUTHVALUE_TRUE = 1;
  104. /**
  105. * Definition of an SNMP return type 'TruthValue'
  106. * @var Definition of an SNMP return type 'TruthValue'
  107. */
  108. const SNMP_TRUTHVALUE_FALSE = 2;
  109. /**
  110. * PHP equivalents of SNMP return type TruthValue
  111. * @var array PHP equivalents of SNMP return type TruthValue
  112. */
  113. public static $SNMP_TRUTHVALUES = array(
  114. self::SNMP_TRUTHVALUE_TRUE => true,
  115. self::SNMP_TRUTHVALUE_FALSE => false
  116. );
  117. /**
  118. * The constructor.
  119. *
  120. * @param string $host The target host for SNMP queries.
  121. * @param string $community The community to use for SNMP queries.
  122. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  123. */
  124. public function __construct( $host = '127.0.0.1', $community = 'public' )
  125. {
  126. return $this->setHost( $host )
  127. ->setCommunity( $community )
  128. ->setOidOutputFormat( self::OID_OUTPUT_NUMERIC );
  129. }
  130. /**
  131. * Proxy to the snmp2_real_walk command
  132. *
  133. * @param string $oid The OID to walk
  134. * @return array The results of the walk
  135. */
  136. public function realWalk( $oid )
  137. {
  138. return $this->_lastResult = @snmp2_real_walk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
  139. }
  140. /**
  141. * Get a single SNMP value
  142. *
  143. * @throws \OSS_SNMP\Exception On *any* SNMP error, warnings are supressed and a generic exception is thrown
  144. * @param string $oid The OID to get
  145. * @return mixed The resultant value
  146. */
  147. public function get( $oid )
  148. {
  149. if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
  150. return $rtn;
  151. $this->_lastResult = @snmp2_get( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
  152. if( $this->_lastResult === false )
  153. throw new Exception( 'Cound not perform walk for OID ' . $oid );
  154. return $this->getCache()->save( $oid, $this->parseSnmpValue( $this->_lastResult ) );
  155. }
  156. /**
  157. * Get indexed SNMP values (first degree)
  158. *
  159. * Walks the SNMP tree returning an array of key => value pairs.
  160. *
  161. * This is a first degree walk and it will throw an exception if there is more that one degree of values.
  162. *
  163. * I.e. the following query with sample results:
  164. *
  165. * walk1d( '.1.0.8802.1.1.2.1.3.7.1.4' )
  166. *
  167. * .1.0.8802.1.1.2.1.3.7.1.4.1 = STRING: "GigabitEthernet1/0/1"
  168. * .1.0.8802.1.1.2.1.3.7.1.4.2 = STRING: "GigabitEthernet1/0/2"
  169. * .1.0.8802.1.1.2.1.3.7.1.4.3 = STRING: "GigabitEthernet1/0/3"
  170. * .....
  171. *
  172. * would yield an array:
  173. *
  174. * 1 => GigabitEthernet1/0/1
  175. * 2 => GigabitEthernet1/0/2
  176. * 3 => GigabitEthernet1/0/3
  177. *
  178. * @param string $oid The OID to walk
  179. * @return array The resultant values
  180. * @throws \OSS_SNMPException On *any* SNMP error, warnings are supressed and a generic exception is thrown
  181. */
  182. public function walk1d( $oid )
  183. {
  184. if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
  185. return $rtn;
  186. $this->_lastResult = @snmp2_real_walk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
  187. if( $this->_lastResult === false )
  188. throw new Exception( 'Cound not perform walk for OID ' . $oid );
  189. $result = array();
  190. $oidPrefix = null;
  191. foreach( $this->_lastResult as $_oid => $value )
  192. {
  193. if( $oidPrefix !== null && $oidPrefix != substr( $_oid, 0, strrpos( $_oid, '.' ) ) )
  194. throw new Exception( 'Requested OID tree is not a first degree indexed SNMP value' );
  195. else
  196. $oidPrefix = substr( $_oid, 0, strrpos( $_oid, '.' ) );
  197. $result[ substr( $_oid, strrpos( $_oid, '.' ) + 1 ) ] = $this->parseSnmpValue( $value );
  198. }
  199. return $this->getCache()->save( $oid, $result );
  200. }
  201. /**
  202. * Get indexed SNMP values where the array key is the given position of the OID
  203. *
  204. * I.e. the following query with sample results:
  205. *
  206. * subOidWalk( '.1.3.6.1.4.1.9.9.23.1.2.1.1.9', 15 )
  207. *
  208. *
  209. * .1.3.6.1.4.1.9.9.23.1.2.1.1.9.10101.5 = Hex-STRING: 00 00 00 01
  210. * .1.3.6.1.4.1.9.9.23.1.2.1.1.9.10105.2 = Hex-STRING: 00 00 00 01
  211. * .1.3.6.1.4.1.9.9.23.1.2.1.1.9.10108.4 = Hex-STRING: 00 00 00 01
  212. *
  213. * would yield an array:
  214. *
  215. * 10101 => Hex-STRING: 00 00 00 01
  216. * 10105 => Hex-STRING: 00 00 00 01
  217. * 10108 => Hex-STRING: 00 00 00 01
  218. *
  219. * @throws \OSS_SNMP\Exception On *any* SNMP error, warnings are supressed and a generic exception is thrown
  220. * @param string $oid The OID to walk
  221. * @param int $position The position of the OID to use as the key
  222. * @return array The resultant values
  223. */
  224. public function subOidWalk( $oid, $position )
  225. {
  226. if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
  227. return $rtn;
  228. $this->_lastResult = @snmp2_real_walk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
  229. if( $this->_lastResult === false )
  230. throw new Exception( 'Cound not perform walk for OID ' . $oid );
  231. $result = array();
  232. foreach( $this->_lastResult as $_oid => $value )
  233. {
  234. $oids = explode( '.', $_oid );
  235. $result[ $oids[ $position] ] = $this->parseSnmpValue( $value );
  236. }
  237. return $this->getCache()->save( $oid, $result );
  238. }
  239. /**
  240. * Get indexed SNMP values where they are indexed by IPv4 addresses
  241. *
  242. * I.e. the following query with sample results:
  243. *
  244. * subOidWalk( '.1.3.6.1.2.1.15.3.1.1. )
  245. *
  246. *
  247. * .1.3.6.1.2.1.15.3.1.1.10.20.30.4 = IpAddress: 192.168.10.10
  248. * ...
  249. *
  250. * would yield an array:
  251. *
  252. * [10.20.30.4] => "192.168.10.10"
  253. * ....
  254. *
  255. * @throws \OSS_SNMP\Exception On *any* SNMP error, warnings are supressed and a generic exception is thrown
  256. * @param string $oid The OID to walk
  257. * @return array The resultant values
  258. */
  259. public function walkIPv4( $oid )
  260. {
  261. if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
  262. return $rtn;
  263. $this->_lastResult = @snmp2_real_walk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
  264. if( $this->_lastResult === false )
  265. throw new Exception( 'Cound not perform walk for OID ' . $oid );
  266. $result = array();
  267. foreach( $this->_lastResult as $_oid => $value )
  268. {
  269. $oids = explode( '.', $_oid );
  270. $len = count( $oids );
  271. $result[ $oids[ $len - 4 ] . '.' . $oids[ $len - 3 ] . '.' . $oids[ $len - 2 ] . '.' . $oids[ $len - 1 ] ] = $this->parseSnmpValue( $value );
  272. }
  273. return $this->getCache()->save( $oid, $result );
  274. }
  275. /**
  276. * Parse the result of an SNMP query into a PHP type
  277. *
  278. * For example, [STRING: "blah"] is parsed to a PHP string containing: blah
  279. *
  280. * @param string $v The value to parse
  281. * @return mixed The parsed value
  282. * @throws Exception
  283. */
  284. public function parseSnmpValue( $v )
  285. {
  286. // first, rule out an empty string
  287. if( $v == '""' || $v == '' )
  288. return "";
  289. $type = substr( $v, 0, strpos( $v, ':' ) );
  290. $value = trim( substr( $v, strpos( $v, ':' ) + 1 ) );
  291. switch( $type )
  292. {
  293. case 'STRING':
  294. if( substr( $value, 0, 1 ) == '"' )
  295. $rtn = (string)trim( substr( substr( $value, 1 ), 0, -1 ) );
  296. else
  297. $rtn = (string)$value;
  298. break;
  299. case 'INTEGER':
  300. if( !is_numeric( $value ) )
  301. $rtn = (float)substr( substr( $value, strpos( $value, '(' ) + 1 ), 0, -1 );
  302. else
  303. $rtn = (float)$value;
  304. break;
  305. case 'Counter32':
  306. case 'Counter64':
  307. $rtn = (int)$value;
  308. break;
  309. case 'Gauge32':
  310. case 'Gauge64':
  311. $rtn = (int)$value;
  312. break;
  313. case 'Hex-STRING':
  314. $rtn = (string)implode( '', explode( ' ', $value ) );
  315. break;
  316. case 'IpAddress':
  317. $rtn = (string)$value;
  318. break;
  319. case 'Timeticks':
  320. $rtn = (int)substr( $value, 1, strrpos( $value, ')' ) - 1 );
  321. break;
  322. default:
  323. throw new Exception( "ERR: Unhandled SNMP return type: $type\n" );
  324. }
  325. return $rtn;
  326. }
  327. /**
  328. * Utility function to convert TruthValue SNMP responses to true / false
  329. *
  330. * @param integer $value The TruthValue ( 1 => true, 2 => false) to convert
  331. * @return boolean
  332. */
  333. public static function ppTruthValue( $value )
  334. {
  335. if( is_array( $value ) )
  336. foreach( $value as $k => $v )
  337. $value[$k] = self::$SNMP_TRUTHVALUES[ $v ];
  338. else
  339. $value = self::$SNMP_TRUTHVALUES[ $value ];
  340. return $value;
  341. }
  342. /**
  343. * Utility function to translate one value(s) to another via an associated array
  344. *
  345. * I.e. all elements '$value' will be replaced with $translator( $value ) where
  346. * $translator is an associated array.
  347. *
  348. * Huh? Just read the code below!
  349. *
  350. * @param mixed $values A scalar or array or values to translate
  351. * @param array $translator An associated array to use to translate the values
  352. * @return mixed The translated scalar or array
  353. */
  354. public static function translate( $values, $translator )
  355. {
  356. if( !is_array( $values ) )
  357. {
  358. if( isset( $translator[ $values ] ) )
  359. return $translator[ $values ];
  360. else
  361. return "*** UNKNOWN ***";
  362. }
  363. foreach( $values as $k => $v )
  364. {
  365. if( isset( $translator[ $v ] ) )
  366. $values[$k] = $translator[ $v ];
  367. else
  368. $values[$k] = "*** UNKNOWN ***";
  369. }
  370. return $values;
  371. }
  372. /**
  373. * Sets the output format for SNMP queries.
  374. *
  375. * Should be one of the class OID_OUTPUT_* constants
  376. *
  377. * @param int $f The fomat to use
  378. * @return OSS_SNMP\SNMP An instance of $this (for fluent interfaces)
  379. */
  380. public function setOidOutputFormat( $f )
  381. {
  382. snmp_set_oid_output_format( $f );
  383. return $this;
  384. }
  385. /**
  386. * Sets the target host for SNMP queries.
  387. *
  388. * @param string $h The target host for SNMP queries.
  389. * @return \OSS_SNMP\SNMP An instance of $this (for fluent interfaces)
  390. */
  391. public function setHost( $h )
  392. {
  393. $this->_host = $h;
  394. // clear the temporary result cache and last result
  395. $this->_lastResult = null;
  396. unset( $this->_resultCache );
  397. $this->_resultCache = array();
  398. return $this;
  399. }
  400. /**
  401. * Returns the target host as currently configured for SNMP queries
  402. *
  403. * @return string The target host as currently configured for SNMP queries
  404. */
  405. public function getHost()
  406. {
  407. return $this->_host;
  408. }
  409. /**
  410. * Sets the community string to use for SNMP queries.
  411. *
  412. * @param string $c The community to use for SNMP queries.
  413. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  414. */
  415. public function setCommunity( $c )
  416. {
  417. $this->_community = $c;
  418. return $this;
  419. }
  420. /**
  421. * Returns the community string currently in use.
  422. *
  423. * @return string The community string currently in use.
  424. */
  425. public function getCommunity()
  426. {
  427. return $this->_community;
  428. }
  429. /**
  430. * Sets the timeout to use for SNMP queries (microseconds).
  431. *
  432. * @param int $t The timeout to use for SNMP queries (microseconds).
  433. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  434. */
  435. public function setTimeout( $t )
  436. {
  437. $this->_timeout = $t;
  438. return $this;
  439. }
  440. /**
  441. * Returns the SNMP query timeout (microseconds).
  442. *
  443. * @return int The the SNMP query timeout (microseconds)
  444. */
  445. public function getTimeout()
  446. {
  447. return $this->_timeout;
  448. }
  449. /**
  450. * Sets the SNMP query retry count.
  451. *
  452. * @param int $r The SNMP query retry count
  453. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  454. */
  455. public function setRetry( $r )
  456. {
  457. $this->_retry = $r;
  458. return $this;
  459. }
  460. /**
  461. * Returns the SNMP query retry count
  462. *
  463. * @return string The SNMP query retry count
  464. */
  465. public function getRetry()
  466. {
  467. return $this->_retry;
  468. }
  469. /**
  470. * Returns the unaltered original last SNMP result
  471. *
  472. * @return mixed The unaltered original last SNMP result
  473. */
  474. public function getLastResult()
  475. {
  476. return $this->_lastResult;
  477. }
  478. /**
  479. * Returns the internal result cache
  480. *
  481. * @return array The internal result cache
  482. */
  483. public function getResultCache()
  484. {
  485. return $this->_resultCache;
  486. }
  487. /**
  488. * Disable lookups of the local cache
  489. *
  490. * @return SNMP An instance of this for fluent interfaces
  491. */
  492. public function disableCache()
  493. {
  494. $this->_disableCache = true;
  495. return $this;
  496. }
  497. /**
  498. * Enable lookups of the local cache
  499. *
  500. * @return SNMP An instance of this for fluent interfaces
  501. */
  502. public function enableCache()
  503. {
  504. $this->_disableCache = false;
  505. return $this;
  506. }
  507. /**
  508. * Query whether we are using the cache or not
  509. *
  510. * @return boolean True of the local lookup cache is enabled. Otherwise false.
  511. */
  512. public function cache()
  513. {
  514. return !$this->_disableCache;
  515. }
  516. /**
  517. * Set the cache to use
  518. *
  519. * @param \OSS_SNMP\Cache $c The cache to use
  520. * @return \OSS_SNMP\SNMP For fluent interfaces
  521. */
  522. public function setCache( $c )
  523. {
  524. $this->_cache = $c;
  525. return $this;
  526. }
  527. /**
  528. * Get the cache in use (or create a Cache\Basic instance
  529. *
  530. * We kind of mandate the use of a cache as the code is written with a cache in mind.
  531. * You are free to disable it via disableCache() but your machines may be hammered!
  532. *
  533. * We would suggest disableCache() / enableCache() used in pairs only when really needed.
  534. *
  535. * @return \OSS_SNMP\Cache The cache object
  536. */
  537. public function getCache()
  538. {
  539. if( $this->_cache === null )
  540. $this->_cache = new \OSS_SNMP\Cache\Basic();
  541. return $this->_cache;
  542. }
  543. /**
  544. * Magic method for generic function calls
  545. *
  546. * @param string $method
  547. * @param array $args
  548. * @throws Exception
  549. */
  550. public function __call( $method, $args )
  551. {
  552. if( substr( $method, 0, 3 ) == 'use' )
  553. return $this->useExtension( substr( $method, 3 ), $args );
  554. throw new Exception( "ERR: Unknown method requested in magic __call(): $method\n" );
  555. }
  556. /**
  557. * This is the MIB Extension magic
  558. *
  559. * Calling $this->useXXX_YYY_ZZZ()->fn() will instantiate
  560. * an extension MIB class is the given name and this $this SNMP
  561. * instance and then call fn().
  562. *
  563. * See the examples for more information.
  564. *
  565. * @param string $mib The extension class to use
  566. * @param array $args
  567. * @return \OSS_SNMP\MIBS
  568. */
  569. public function useExtension( $mib, $args )
  570. {
  571. $mib = '\\OSS_SNMP\\MIBS\\' . str_replace( '_', '\\', $mib );
  572. $m = new $mib();
  573. $m->setSNMP( $this );
  574. return $m;
  575. }
  576. }