SNMP.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  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. * @throws \OSS\Exception On *any* SNMP error, warnings are supressed and a generic exception is thrown
  133. * @param string $oid The OID to get
  134. * @return mixed The resultant value
  135. */
  136. public function get( $oid )
  137. {
  138. if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
  139. return $rtn;
  140. $this->_lastResult = @snmp2_get( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
  141. if( $this->_lastResult === false )
  142. throw new Exception( 'Cound not perform walk for OID ' . $oid );
  143. return $this->getCache()->save( $oid, $this->parseSnmpValue( $this->_lastResult ) );
  144. }
  145. /**
  146. * Get indexed SNMP values (first degree)
  147. *
  148. * Walks the SNMP tree returning an array of key => value pairs.
  149. *
  150. * This is a first degree walk and it will throw an exception if there is more that one degree of values.
  151. *
  152. * I.e. the following query with sample results:
  153. *
  154. * walk1d( '.1.0.8802.1.1.2.1.3.7.1.4' )
  155. *
  156. * .1.0.8802.1.1.2.1.3.7.1.4.1 = STRING: "GigabitEthernet1/0/1"
  157. * .1.0.8802.1.1.2.1.3.7.1.4.2 = STRING: "GigabitEthernet1/0/2"
  158. * .1.0.8802.1.1.2.1.3.7.1.4.3 = STRING: "GigabitEthernet1/0/3"
  159. * .....
  160. *
  161. * would yield an array:
  162. *
  163. * 1 => GigabitEthernet1/0/1
  164. * 2 => GigabitEthernet1/0/2
  165. * 3 => GigabitEthernet1/0/3
  166. *
  167. * @param string $oid The OID to walk
  168. * @return array The resultant values
  169. * @throws \OSS\Exception On *any* SNMP error, warnings are supressed and a generic exception is thrown
  170. */
  171. public function walk1d( $oid )
  172. {
  173. if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
  174. return $rtn;
  175. $this->_lastResult = @snmp2_real_walk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
  176. if( $this->_lastResult === false )
  177. throw new Exception( 'Cound not perform walk for OID ' . $oid );
  178. $result = array();
  179. $oidPrefix = null;
  180. foreach( $this->_lastResult as $_oid => $value )
  181. {
  182. if( $oidPrefix !== null && $oidPrefix != substr( $_oid, 0, strrpos( $_oid, '.' ) ) )
  183. throw new Exception( 'Requested OID tree is not a first degree indexed SNMP value' );
  184. else
  185. $oidPrefix = substr( $_oid, 0, strrpos( $_oid, '.' ) );
  186. $result[ substr( $_oid, strrpos( $_oid, '.' ) + 1 ) ] = $this->parseSnmpValue( $value );
  187. }
  188. return $this->getCache()->save( $oid, $result );
  189. }
  190. /**
  191. * Get indexed SNMP values where the array key is the given position of the OID
  192. *
  193. * I.e. the following query with sample results:
  194. *
  195. * subOidWalk( '.1.3.6.1.4.1.9.9.23.1.2.1.1.9', 15 )
  196. *
  197. *
  198. * .1.3.6.1.4.1.9.9.23.1.2.1.1.9.10101.5 = Hex-STRING: 00 00 00 01
  199. * .1.3.6.1.4.1.9.9.23.1.2.1.1.9.10105.2 = Hex-STRING: 00 00 00 01
  200. * .1.3.6.1.4.1.9.9.23.1.2.1.1.9.10108.4 = Hex-STRING: 00 00 00 01
  201. *
  202. * would yield an array:
  203. *
  204. * 10101 => Hex-STRING: 00 00 00 01
  205. * 10105 => Hex-STRING: 00 00 00 01
  206. * 10108 => Hex-STRING: 00 00 00 01
  207. *
  208. * @throws \OSS\Exception On *any* SNMP error, warnings are supressed and a generic exception is thrown
  209. * @param string $oid The OID to walk
  210. * @param int $position The position of the OID to use as the key
  211. * @return array The resultant values
  212. */
  213. public function subOidWalk( $oid, $position )
  214. {
  215. if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
  216. return $rtn;
  217. $this->_lastResult = @snmp2_real_walk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
  218. if( $this->_lastResult === false )
  219. throw new Exception( 'Cound not perform walk for OID ' . $oid );
  220. $result = array();
  221. foreach( $this->_lastResult as $_oid => $value )
  222. {
  223. $oids = explode( '.', $_oid );
  224. $result[ $oids[ $position] ] = $this->parseSnmpValue( $value );
  225. }
  226. return $this->getCache()->save( $oid, $result );
  227. }
  228. /**
  229. * Parse the result of an SNMP query into a PHP type
  230. *
  231. * For example, [STRING: "blah"] is parsed to a PHP string containing: blah
  232. *
  233. * @param string $v The value to parse
  234. * @return mixed The parsed value
  235. * @throws Exception
  236. */
  237. public function parseSnmpValue( $v )
  238. {
  239. // first, rule out an empty string
  240. if( $v == '""' || $v == '' )
  241. return "";
  242. $type = substr( $v, 0, strpos( $v, ':' ) );
  243. $value = trim( substr( $v, strpos( $v, ':' ) + 1 ) );
  244. switch( $type )
  245. {
  246. case 'STRING':
  247. if( substr( $value, 0, 1 ) == '"' )
  248. $rtn = (string)substr( substr( $value, 1 ), 0, -1 );
  249. else
  250. $rtn = (string)$value;
  251. break;
  252. case 'INTEGER':
  253. if( !is_numeric( $value ) )
  254. $rtn = (int)substr( substr( $value, strpos( $value, '(' ) + 1 ), 0, -1 );
  255. else
  256. $rtn = (int)$value;
  257. break;
  258. case 'Counter32':
  259. $rtn = (int)$value;
  260. break;
  261. case 'Gauge32':
  262. $rtn = (int)$value;
  263. break;
  264. case 'Hex-STRING':
  265. $rtn = (string)implode( '', explode( ' ', $value ) );
  266. break;
  267. case 'Timeticks':
  268. $rtn = substr( $value, 1, strrpos( $value, ')' ) - 1 );
  269. break;
  270. default:
  271. throw new Exception( "ERR: Unhandled SNMP return type: $type\n" );
  272. }
  273. return $rtn;
  274. }
  275. /**
  276. * Utility function to convert TruthValue SNMP responses to true / false
  277. *
  278. * @param integer $value The TruthValue ( 1 => true, 2 => false) to convert
  279. * @return boolean
  280. */
  281. public static function ppTruthValue( $value )
  282. {
  283. if( is_array( $value ) )
  284. foreach( $value as $k => $v )
  285. $value[$k] = self::$SNMP_TRUTHVALUES[ $v ];
  286. else
  287. $value = self::$SNMP_TRUTHVALUES[ $value ];
  288. return $value;
  289. }
  290. /**
  291. * Utility function to translate one value(s) to another via an associated array
  292. *
  293. * I.e. all elements '$value' will be replaced with $translator( $value ) where
  294. * $translator is an associated array.
  295. *
  296. * Huh? Just read the code below!
  297. *
  298. * @param mixed $values A scalar or array or values to translate
  299. * @param array $translator An associated array to use to translate the values
  300. * @return mixed The translated scalar or array
  301. */
  302. public static function translate( $values, $translator )
  303. {
  304. if( !is_array( $values ) )
  305. return $translator[ $values ];
  306. foreach( $values as $k => $v )
  307. $values[$k] = $translator[ $v ];
  308. return $values;
  309. }
  310. /**
  311. * Sets the output format for SNMP queries.
  312. *
  313. * Should be one of the class OID_OUTPUT_* constants
  314. *
  315. * @param int $f The fomat to use
  316. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  317. */
  318. public function setOidOutputFormat( $f )
  319. {
  320. snmp_set_oid_output_format( $f );
  321. return $this;
  322. }
  323. /**
  324. * Sets the target host for SNMP queries.
  325. *
  326. * @param string $h The target host for SNMP queries.
  327. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  328. */
  329. public function setHost( $h )
  330. {
  331. $this->_host = $h;
  332. // clear the temporary result cache and last result
  333. $this->_lastResult = null;
  334. unset( $this->_resultCache );
  335. $this->_resultCache = array();
  336. return $this;
  337. }
  338. /**
  339. * Returns the target host as currently configured for SNMP queries
  340. *
  341. * @return string The target host as currently configured for SNMP queries
  342. */
  343. public function getHost()
  344. {
  345. return $this->_host;
  346. }
  347. /**
  348. * Sets the community string to use for SNMP queries.
  349. *
  350. * @param string $c The community to use for SNMP queries.
  351. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  352. */
  353. public function setCommunity( $c )
  354. {
  355. $this->_community = $c;
  356. return $this;
  357. }
  358. /**
  359. * Returns the community string currently in use.
  360. *
  361. * @return string The community string currently in use.
  362. */
  363. public function getCommunity()
  364. {
  365. return $this->_community;
  366. }
  367. /**
  368. * Sets the timeout to use for SNMP queries (microseconds).
  369. *
  370. * @param int $t The timeout to use for SNMP queries (microseconds).
  371. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  372. */
  373. public function setTimeout( $t )
  374. {
  375. $this->_timeout = $t;
  376. return $this;
  377. }
  378. /**
  379. * Returns the SNMP query timeout (microseconds).
  380. *
  381. * @return int The the SNMP query timeout (microseconds)
  382. */
  383. public function getTimeout()
  384. {
  385. return $this->_timeout;
  386. }
  387. /**
  388. * Sets the SNMP query retry count.
  389. *
  390. * @param int $r The SNMP query retry count
  391. * @return OSS_SNMP An instance of $this (for fluent interfaces)
  392. */
  393. public function setRetry( $r )
  394. {
  395. $this->_retry = $r;
  396. return $this;
  397. }
  398. /**
  399. * Returns the SNMP query retry count
  400. *
  401. * @return string The SNMP query retry count
  402. */
  403. public function getRetry()
  404. {
  405. return $this->_retry;
  406. }
  407. /**
  408. * Returns the unaltered original last SNMP result
  409. *
  410. * @return mixed The unaltered original last SNMP result
  411. */
  412. public function getLastResult()
  413. {
  414. return $this->_lastResult;
  415. }
  416. /**
  417. * Returns the internal result cache
  418. *
  419. * @return array The internal result cache
  420. */
  421. public function getResultCache()
  422. {
  423. return $this->_resultCache;
  424. }
  425. /**
  426. * Disable lookups of the local cache
  427. *
  428. * @return SNMP An instance of this for fluent interfaces
  429. */
  430. public function disableCache()
  431. {
  432. $this->_disableCache = true;
  433. return $this;
  434. }
  435. /**
  436. * Enable lookups of the local cache
  437. *
  438. * @return SNMP An instance of this for fluent interfaces
  439. */
  440. public function enableCache()
  441. {
  442. $this->_disableCache = false;
  443. return $this;
  444. }
  445. /**
  446. * Query whether we are using the cache or not
  447. *
  448. * @return boolean True of the local lookup cache is enabled. Otherwise false.
  449. */
  450. public function cache()
  451. {
  452. return !$this->_disableCache;
  453. }
  454. /**
  455. * Set the cache to use
  456. *
  457. * @param \OSS\Cache $c The cache to use
  458. * @return \OSS\SNMP For fluent interfaces
  459. */
  460. public function setCache( $c )
  461. {
  462. $this->_cache = $c;
  463. return $this;
  464. }
  465. /**
  466. * Get the cache in use (or create a Cache\Basic instance
  467. *
  468. * We kind of mandate the use of a cache as the code is written with a cache in mind.
  469. * You are free to disable it via disableCache() but your machines may be hammered!
  470. *
  471. * We would suggest disableCache() / enableCache() used in pairs only when really needed.
  472. *
  473. * @return \OSS\Cache The cache object
  474. */
  475. public function getCache()
  476. {
  477. if( $this->_cache === null )
  478. $this->_cache = new \OSS\Cache\Basic();
  479. return $this->_cache;
  480. }
  481. /**
  482. * Magic method for generic function calls
  483. *
  484. * @param string $method
  485. * @param array $args
  486. * @throws Exception
  487. */
  488. public function __call( $method, $args )
  489. {
  490. if( substr( $method, 0, 3 ) == 'use' )
  491. return $this->useExtension( substr( $method, 3 ), $args );
  492. throw new Exception( "ERR: Unknown method requested in magic __call(): $method\n" );
  493. }
  494. /**
  495. * This is the MIB Extension magic
  496. *
  497. * Calling $this->useXXX_YYY_ZZZ()->fn() will instantiate
  498. * an extension MIB class is the given name and this $this SNMP
  499. * instance and then call fn().
  500. *
  501. * See the examples for more information.
  502. *
  503. * @param string $mib The extension class to use
  504. * @param array $args
  505. * @return \OSS\SNMP\MIBS
  506. */
  507. public function useExtension( $mib, $args )
  508. {
  509. $mib = '\\OSS\\SNMP\\MIBS\\' . str_replace( '_', '\\', $mib );
  510. $m = new $mib();
  511. $m->setSNMP( $this );
  512. return $m;
  513. }
  514. }