SNMP.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  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 <COPYRIGHT HOLDER> 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;
  30. spl_autoload_register( function( $class ) {
  31. if( substr( $class, 0, 4 ) == 'OSS\\' )
  32. {
  33. $class = str_replace( '\\', '/', $class );
  34. require( dirname( __FILE__ ) . '/../' . $class . '.php' );
  35. }
  36. });
  37. /**
  38. * A class for performing SNMP V2 queries and processing the results.
  39. *
  40. * @copyright Copyright (c) 2012, Open Source Solutions Limited, Dublin, Ireland
  41. * @author Barry O'Donovan <barry@opensolutions.ie>
  42. */
  43. class SNMP
  44. {
  45. /** @type string The SNMP community to use when polling SNMP services. Defaults to 'public' by the constructor. */
  46. protected $_community;
  47. /**
  48. * The SNMP host to query. Defaults to '127.0.0.1'
  49. * @var string The SNMP host to query. Defaults to '127.0.0.1' by the constructor.
  50. */
  51. protected $_host;
  52. /**
  53. * The SNMP query timeout value (microseconds). Default: 1000000
  54. * @var int The SNMP query timeout value (microseconds). Default: 1000000
  55. */
  56. protected $_timeout = 1000000;
  57. /**
  58. * The SNMP query retry count. Default: 5
  59. * @var int The SNMP query retry count. Default: 5
  60. */
  61. protected $_retry = 5;
  62. /**
  63. * A variable to hold the last unaltered result of an SNMP query
  64. * @var mixed The last unaltered result of an SNMP query
  65. */
  66. protected $_lastResult = null;
  67. /**
  68. * The cache object to use as the cache
  69. * @var \OSS\Cache The cache object to use
  70. */
  71. protected $_cache = null;
  72. /**
  73. * Set to true to disable local cache lookup and force SNMP queries
  74. *
  75. * Results are still stored. If you need to force a SNMP query, you can:
  76. *
  77. * $snmp = new OSS\SNMP( ... )'
  78. * ...
  79. * $snmp->disableCache();
  80. * $snmp->get( ... );
  81. * $snmp->enableCache();
  82. */
  83. protected $_disableCache = false;
  84. /*
  85. * SNMP output constants to mirror those of PHP
  86. */
  87. const OID_OUTPUT_FULL = SNMP_OID_OUTPUT_FULL;
  88. const OID_OUTPUT_NUMERIC = SNMP_OID_OUTPUT_NUMERIC;
  89. /**
  90. * Definition of an SNMP return type 'TruthValue'
  91. */
  92. const SNMP_TRUTHVALUE_TRUE = 1;
  93. /**
  94. * Definition of an SNMP return type 'TruthValue'
  95. */
  96. const SNMP_TRUTHVALUE_FALSE = 2;
  97. /**
  98. * PHP equivalents of SNMP return type TruthValue
  99. *
  100. * @var array PHP equivalents of SNMP return type TruthValue
  101. */
  102. public static $SNMP_TRUTHVALUES = array(
  103. self::SNMP_TRUTHVALUE_TRUE => true,
  104. self::SNMP_TRUTHVALUE_FALSE => false
  105. );
  106. /**
  107. * The constructor.
  108. *
  109. * @param string $host The target host for SNMP queries.
  110. * @param string $community The community to use for SNMP queries.
  111. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  112. */
  113. public function __construct( $host = '127.0.0.1', $community = 'public' )
  114. {
  115. return $this->setHost( $host )
  116. ->setCommunity( $community )
  117. ->setOidOutputFormat( self::OID_OUTPUT_NUMERIC );
  118. }
  119. /**
  120. * Proxy to the snmp2_real_walk command
  121. *
  122. * @param string $oid The OID to walk
  123. * @return array The results of the walk
  124. */
  125. public function realWalk( $oid )
  126. {
  127. return $this->_lastResult = snmp2_real_walk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
  128. }
  129. /**
  130. * Get a single SNMP value
  131. *
  132. * @param string $oid The OID to get
  133. * @return mixed The resultant value
  134. */
  135. public function get( $oid )
  136. {
  137. if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
  138. return $rtn;
  139. $this->_lastResult = snmp2_get( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
  140. return $this->getCache()->save( $oid, $this->parseSnmpValue( $this->_lastResult ) );
  141. }
  142. /**
  143. * Get indexed SNMP values (first degree)
  144. *
  145. * Walks the SNMP tree returning an array of key => value pairs.
  146. *
  147. * This is a first degree walk and it will throw an exception if there is more that one degree of values.
  148. *
  149. * I.e. the following query with sample results:
  150. *
  151. * walk1d( '.1.0.8802.1.1.2.1.3.7.1.4' )
  152. *
  153. * .1.0.8802.1.1.2.1.3.7.1.4.1 = STRING: "GigabitEthernet1/0/1"
  154. * .1.0.8802.1.1.2.1.3.7.1.4.2 = STRING: "GigabitEthernet1/0/2"
  155. * .1.0.8802.1.1.2.1.3.7.1.4.3 = STRING: "GigabitEthernet1/0/3"
  156. * .....
  157. *
  158. * would yield an array:
  159. *
  160. * 1 => GigabitEthernet1/0/1
  161. * 2 => GigabitEthernet1/0/2
  162. * 3 => GigabitEthernet1/0/3
  163. *
  164. * @param string $oid The OID to walk
  165. * @return array The resultant values
  166. */
  167. public function walk1d( $oid )
  168. {
  169. if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
  170. return $rtn;
  171. $this->_lastResult = snmp2_real_walk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
  172. $result = array();
  173. $oidPrefix = null;
  174. foreach( $this->_lastResult as $_oid => $value )
  175. {
  176. if( $oidPrefix !== null && $oidPrefix != substr( $_oid, 0, strrpos( $_oid, '.' ) ) )
  177. throw new Exception( 'Requested OID tree is not a first degree indexed SNMP value' );
  178. else
  179. $oidPrefix = substr( $_oid, 0, strrpos( $_oid, '.' ) );
  180. $result[ substr( $_oid, strrpos( $_oid, '.' ) + 1 ) ] = $this->parseSnmpValue( $value );
  181. }
  182. return $this->getCache()->save( $oid, $result );
  183. }
  184. /**
  185. * Get indexed SNMP values where the array key is the given position of the OID
  186. *
  187. * I.e. the following query with sample results:
  188. *
  189. * subOidWalk( '.1.3.6.1.4.1.9.9.23.1.2.1.1.9', 15 )
  190. *
  191. *
  192. * .1.3.6.1.4.1.9.9.23.1.2.1.1.9.10101.5 = Hex-STRING: 00 00 00 01
  193. * .1.3.6.1.4.1.9.9.23.1.2.1.1.9.10105.2 = Hex-STRING: 00 00 00 01
  194. * .1.3.6.1.4.1.9.9.23.1.2.1.1.9.10108.4 = Hex-STRING: 00 00 00 01
  195. *
  196. * would yield an array:
  197. *
  198. * 10101 => Hex-STRING: 00 00 00 01
  199. * 10105 => Hex-STRING: 00 00 00 01
  200. * 10108 => Hex-STRING: 00 00 00 01
  201. *
  202. * @param string $oid The OID to walk
  203. * @param int $position The position of the OID to use as the key
  204. * @return array The resultant values
  205. */
  206. public function subOidWalk( $oid, $position )
  207. {
  208. if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
  209. return $rtn;
  210. $this->_lastResult = snmp2_real_walk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
  211. $result = array();
  212. foreach( $this->_lastResult as $_oid => $value )
  213. {
  214. $oids = explode( '.', $_oid );
  215. $result[ $oids[ $position] ] = $this->parseSnmpValue( $value );
  216. }
  217. return $this->getCache()->save( $oid, $result );
  218. }
  219. /**
  220. * Parse the result of an SNMP query into a PHP type
  221. *
  222. * For example, [STRING: "blah"] is parsed to a PHP string containing: blah
  223. *
  224. * @param string $v The value to parse
  225. * @return mixed The parsed value
  226. * @throws Exception
  227. */
  228. public function parseSnmpValue( $v )
  229. {
  230. // first, rule out an empty string
  231. if( $v == '""' || $v == '' )
  232. return "";
  233. $type = substr( $v, 0, strpos( $v, ':' ) );
  234. $value = trim( substr( $v, strpos( $v, ':' ) + 1 ) );
  235. switch( $type )
  236. {
  237. case 'STRING':
  238. if( substr( $value, 0, 1 ) == '"' )
  239. $rtn = (string)substr( substr( $value, 1 ), 0, -1 );
  240. else
  241. $rtn = (string)$value;
  242. break;
  243. case 'INTEGER':
  244. if( !is_numeric( $value ) )
  245. $rtn = (int)substr( substr( $value, strpos( $value, '(' ) + 1 ), 0, -1 );
  246. else
  247. $rtn = (int)$value;
  248. break;
  249. case 'Counter32':
  250. $rtn = (int)$value;
  251. break;
  252. case 'Gauge32':
  253. $rtn = (int)$value;
  254. break;
  255. case 'Hex-STRING':
  256. $rtn = (string)implode( '', explode( ' ', $value ) );
  257. break;
  258. case 'Timeticks':
  259. $rtn = substr( $value, 1, strrpos( $value, ')' ) - 1 );
  260. break;
  261. default:
  262. throw new Exception( "ERR: Unhandled SNMP return type: $type\n" );
  263. }
  264. return $rtn;
  265. }
  266. /**
  267. * Utility function to convert TruthValue SNMP responses to true / false
  268. *
  269. * @param integer $value The TruthValue ( 1 => true, 2 => false) to convert
  270. * @return boolean
  271. */
  272. public static function ppTruthValue( $value )
  273. {
  274. if( is_array( $value ) )
  275. foreach( $value as $k => $v )
  276. $value[$k] = self::$SNMP_TRUTHVALUES[ $v ];
  277. else
  278. $value = self::$SNMP_TRUTHVALUES[ $value ];
  279. return $value;
  280. }
  281. /**
  282. * Utility function to translate one value(s) to another via an associated array
  283. *
  284. * I.e. all elements '$value' will be replaced with $translator( $value ) where
  285. * $translator is an associated array.
  286. *
  287. * Huh? Just read the code below!
  288. *
  289. * @param mixed $values A scalar or array or values to translate
  290. * @param array $translator An associated array to use to translate the values
  291. * @return mixed The translated scalar or array
  292. */
  293. public static function translate( $values, $translator )
  294. {
  295. if( !is_array( $values ) )
  296. return $translator[ $values ];
  297. foreach( $values as $k => $v )
  298. $values[$k] = $translator[ $v ];
  299. return $values;
  300. }
  301. /**
  302. * Sets the output format for SNMP queries.
  303. *
  304. * Should be one of the class OID_OUTPUT_* constants
  305. *
  306. * @param int $f The fomat to use
  307. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  308. */
  309. public function setOidOutputFormat( $f )
  310. {
  311. snmp_set_oid_output_format( $f );
  312. return $this;
  313. }
  314. /**
  315. * Sets the target host for SNMP queries.
  316. *
  317. * @param string $h The target host for SNMP queries.
  318. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  319. */
  320. public function setHost( $h )
  321. {
  322. $this->_host = $h;
  323. // clear the temporary result cache and last result
  324. $this->_lastResult = null;
  325. unset( $this->_resultCache );
  326. $this->_resultCache = array();
  327. return $this;
  328. }
  329. /**
  330. * Returns the target host as currently configured for SNMP queries
  331. *
  332. * @return string The target host as currently configured for SNMP queries
  333. */
  334. public function getHost()
  335. {
  336. return $this->_host;
  337. }
  338. /**
  339. * Sets the community string to use for SNMP queries.
  340. *
  341. * @param string $c The community to use for SNMP queries.
  342. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  343. */
  344. public function setCommunity( $c )
  345. {
  346. $this->_community = $c;
  347. return $this;
  348. }
  349. /**
  350. * Returns the community string currently in use.
  351. *
  352. * @return string The community string currently in use.
  353. */
  354. public function getCommunity()
  355. {
  356. return $this->_community;
  357. }
  358. /**
  359. * Sets the timeout to use for SNMP queries (microseconds).
  360. *
  361. * @param int $t The timeout to use for SNMP queries (microseconds).
  362. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  363. */
  364. public function setTimeout( $t )
  365. {
  366. $this->_timeout = $t;
  367. return $this;
  368. }
  369. /**
  370. * Returns the SNMP query timeout (microseconds).
  371. *
  372. * @return int The the SNMP query timeout (microseconds)
  373. */
  374. public function getTimeout()
  375. {
  376. return $this->_timeout;
  377. }
  378. /**
  379. * Sets the SNMP query retry count.
  380. *
  381. * @param int $r The SNMP query retry count
  382. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  383. */
  384. public function setRetry( $r )
  385. {
  386. $this->_retry = $r;
  387. return $this;
  388. }
  389. /**
  390. * Returns the SNMP query retry count
  391. *
  392. * @return string The SNMP query retry count
  393. */
  394. public function getRetry()
  395. {
  396. return $this->_retry;
  397. }
  398. /**
  399. * Returns the unaltered original last SNMP result
  400. *
  401. * @return mixed The unaltered original last SNMP result
  402. */
  403. public function getLastResult()
  404. {
  405. return $this->_lastResult;
  406. }
  407. /**
  408. * Returns the internal result cache
  409. *
  410. * @return array The internal result cache
  411. */
  412. public function getResultCache()
  413. {
  414. return $this->_resultCache;
  415. }
  416. /**
  417. * Disable lookups of the local cache
  418. *
  419. * @return SNMP An instance of this for fluent interfaces
  420. */
  421. public function disableCache()
  422. {
  423. $this->_disableCache = true;
  424. return $this;
  425. }
  426. /**
  427. * Enable lookups of the local cache
  428. *
  429. * @return SNMP An instance of this for fluent interfaces
  430. */
  431. public function enableCache()
  432. {
  433. $this->_disableCache = false;
  434. return $this;
  435. }
  436. /**
  437. * Query whether we are using the cache or not
  438. *
  439. * @return boolean True of the local lookup cache is enabled. Otherwise false.
  440. */
  441. public function cache()
  442. {
  443. return !$this->_disableCache;
  444. }
  445. /**
  446. * Set the cache to use
  447. *
  448. * @param \OSS\Cache $c The cache to use
  449. * @return \OSS\SNMP For fluent interfaces
  450. */
  451. public function setCache( $c )
  452. {
  453. $this->_cache = $c;
  454. return $this;
  455. }
  456. /**
  457. * Get the cache in use (or create a Cache\Basic instance
  458. *
  459. * We kind of mandate the use of a cache as the code is written with a cache in mind.
  460. * You are free to disable it via disableCache() but your machines may be hammered!
  461. *
  462. * We would suggest disableCache() / enableCache() used in pairs only when really needed.
  463. *
  464. * @return \OSS\Cache The cache object
  465. */
  466. public function getCache()
  467. {
  468. if( $this->_cache === null )
  469. $this->_cache = new \OSS\Cache\Basic();
  470. return $this->_cache;
  471. }
  472. /**
  473. * Magic method for generic function calls
  474. *
  475. * @param string $method
  476. * @param array $args
  477. * @throws Exception
  478. */
  479. public function __call( $method, $args )
  480. {
  481. if( substr( $method, 0, 3 ) == 'use' )
  482. return $this->useExtension( substr( $method, 3 ), $args );
  483. throw new Exception( "ERR: Unknown method requested in magic __call(): $method\n" );
  484. }
  485. /**
  486. * This is the MIB Extension magic
  487. *
  488. * Calling $this->useXXX_YYY_ZZZ()->fn() will instantiate
  489. * an extension MIB class is the given name and this $this SNMP
  490. * instance and then call fn().
  491. *
  492. * See the examples for more information.
  493. *
  494. * @param string $mib The extension class to use
  495. * @param array $args
  496. * @return \OSS\SNMP\MIBS
  497. */
  498. public function useExtension( $mib, $args )
  499. {
  500. $mib = '\\OSS\\SNMP\\MIBS\\' . str_replace( '_', '\\', $mib );
  501. $m = new $mib();
  502. $m->setSNMP( $this );
  503. return $m;
  504. }
  505. }