CDP.php 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  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\MIBS\Cisco;
  30. /**
  31. * A class for performing SNMP V2 queries on Cisco devices
  32. *
  33. * @copyright Copyright (c) 2012, Open Source Solutions Limited, Dublin, Ireland
  34. * @author Barry O'Donovan <barry@opensolutions.ie>
  35. */
  36. class CDP extends \OSS_SNMP\MIBS\Cisco
  37. {
  38. const OID_CDP_INTERFACE_ENABLED = '.1.3.6.1.4.1.9.9.23.1.1.1.1.2';
  39. const OID_CDP_INTERFACE_NAME = '.1.3.6.1.4.1.9.9.23.1.1.1.1.6';
  40. const OID_CDP_CACHE_NEIGHBOUR_ADDRESS_TYPE = '.1.3.6.1.4.1.9.9.23.1.2.1.1.3';
  41. const OID_CDP_CACHE_NEIGHBOUR_ADDRESS = '.1.3.6.1.4.1.9.9.23.1.2.1.1.4';
  42. const OID_CDP_CACHE_NEIGHBOUR_VERSION = '.1.3.6.1.4.1.9.9.23.1.2.1.1.5';
  43. const OID_CDP_CACHE_NEIGHBOUR_ID = '.1.3.6.1.4.1.9.9.23.1.2.1.1.6';
  44. const OID_CDP_CACHE_NEIGHBOUR_PORT = '.1.3.6.1.4.1.9.9.23.1.2.1.1.7';
  45. const OID_CDP_CACHE_NEIGHBOUR_PLATFORM = '.1.3.6.1.4.1.9.9.23.1.2.1.1.8';
  46. const OID_CDP_CACHE_NEIGHBOUR_CAPABILITY = '.1.3.6.1.4.1.9.9.23.1.2.1.1.9';
  47. const OID_CDP_CACHE_NEIGHBOUR_VTP_MGMT_DOMAIN = '.1.3.6.1.4.1.9.9.23.1.2.1.1.10';
  48. const OID_CDP_CACHE_NEIGHBOUR_NATIVE_VLAN = '.1.3.6.1.4.1.9.9.23.1.2.1.1.11';
  49. const OID_CDP_CACHE_NEIGHBOUR_DUPLEX = '.1.3.6.1.4.1.9.9.23.1.2.1.1.12';
  50. const OID_CDP_CACHE_NEIGHBOUR_LAST_CHANGE = '.1.3.6.1.4.1.9.9.23.1.2.1.1.24';
  51. const OID_CDP_GLOBAL_RUN = '.1.3.6.1.4.1.9.9.23.1.3.1.0';
  52. const OID_CDP_GLOBAL_MESSAGE_INTERVAL = '.1.3.6.1.4.1.9.9.23.1.3.2.0';
  53. const OID_CDP_GLOBAL_HOLDTIME = '.1.3.6.1.4.1.9.9.23.1.3.3.0';
  54. const OID_CDP_GLOBAL_DEVICE_ID = '.1.3.6.1.4.1.9.9.23.1.3.4.0';
  55. const OID_CDP_GLOBAL_LAST_CHANGE = '.1.3.6.1.4.1.9.9.23.1.3.5.0';
  56. /**
  57. * Get the device's global CDP (Cisco Discovery Protocol) run status
  58. *
  59. *
  60. * > An indication of whether the Cisco Discovery Protocol is currently
  61. * > running. Entries in cdpCacheTable are deleted when CDP is disabled.
  62. *
  63. * @return boolean True if enabled globally, else false
  64. */
  65. public function globalRun()
  66. {
  67. return $this->getSNMP()->ppTruthValue( $this->getSNMP()->get( self::OID_CDP_GLOBAL_RUN ) );
  68. }
  69. /**
  70. * Get the interval at which CDP messages are to be generated
  71. *
  72. * > The interval at which CDP messages are to be generated.
  73. * > The default value is 60 seconds.
  74. *
  75. * @return int The interval at which CDP messages are to be generated
  76. */
  77. public function globalMessageInterval()
  78. {
  79. return $this->getSNMP()->get( self::OID_CDP_GLOBAL_MESSAGE_INTERVAL );
  80. }
  81. /**
  82. * Get the time for the receiving device holds CDP message
  83. *
  84. * > The time for the receiving device holds CDP message.
  85. * > The default value is 180 seconds."
  86. *
  87. * @return int The time for the receiving device holds CDP message
  88. */
  89. public function globalHoldTime()
  90. {
  91. return $this->getSNMP()->get( self::OID_CDP_GLOBAL_HOLDTIME );
  92. }
  93. /**
  94. * The time when the cache table was last changed
  95. *
  96. * > Indicates the time when the cache table was last changed. It
  97. * > is the most recent time at which any row was last created,
  98. * > modified or deleted.
  99. *
  100. * @return int The time (timeticks) when the cache table was last changed
  101. */
  102. public function globalLastChange()
  103. {
  104. return $this->getSNMP()->get( self::OID_CDP_GLOBAL_LAST_CHANGE );
  105. }
  106. /**
  107. * Get the device's CDP (Cisco Discovery Protocol) ID
  108. *
  109. * @return string The device's CDP (Cisco Discovery Protocol) ID
  110. */
  111. public function id()
  112. {
  113. return $this->getSNMP()->get( self::OID_CDP_GLOBAL_DEVICE_ID );
  114. }
  115. /**
  116. * Get the device's interfaces CDP enabled status
  117. *
  118. * Applies the TruthValue post processor to turn
  119. * SNMP values into true / false.
  120. *
  121. * @see OSS_SNMP\SNMP::ppTruthValue()
  122. *
  123. * @return array The device's interfaces CDP enabled status' (as boolean true / false)
  124. */
  125. public function interfaceEnabled()
  126. {
  127. return $this->getSNMP()->ppTruthValue( $this->getSNMP()->walk1d( self::OID_CDP_INTERFACE_ENABLED ) );
  128. }
  129. /**
  130. * Get the device's interface names as seen in CDP
  131. *
  132. * > The name of the local interface as advertised by CDP in the Port-ID TLV
  133. *
  134. * @return array The device's interface names as seen in CDP
  135. */
  136. public function interfaceNames()
  137. {
  138. return $this->getSNMP()->walk1d( self::OID_CDP_INTERFACE_NAME );
  139. }
  140. /**
  141. * Constant for possible value of CDP neighbour address type
  142. * @see OSS_SNMP\MIBS\Cisco\CDP::neighbourAddressTypes()
  143. */
  144. const CDP_CACHE_NEIGHBOUR_ADDRESS_TYPE_IP = 1;
  145. /**
  146. * Text representation of CDP neighbour address type
  147. *
  148. * @see OSS_SNMP\SNMP\MIBS\Cisco\CDP::neighbourAddressTypes()
  149. * @var array Text representation of CDP neighbour address type
  150. */
  151. public static $CDP_CACHE_NEIGHBOUR_ADDRESS_TYPES = array(
  152. self::CDP_CACHE_NEIGHBOUR_ADDRESS_TYPE_IP => 'ip'
  153. );
  154. /**
  155. * Get the CDP neighbours' address type indexed by the current device's port ID
  156. *
  157. * > An indication of the type of address contained in the corresponding instance of cdpCacheAddress
  158. *
  159. * @param boolean $translate If true, return the string representation via self::$VTP_VLAN_TYPES
  160. * @return array The CDP neighbours' address type indexed by the current device's port ID
  161. */
  162. public function neighbourAddressTypes( $translate = false )
  163. {
  164. $types = $this->getSNMP()->subOidWalk( self::OID_CDP_CACHE_NEIGHBOUR_ADDRESS_TYPE, 15 );
  165. if( !$translate )
  166. return $types;
  167. return $this->getSNMP()->translate( $types, self::$CDP_CACHE_NEIGHBOUR_ADDRESS_TYPES );
  168. }
  169. /**
  170. * Get the device's CDP neighbour addresses indexed by the current device's port ID
  171. *
  172. * > The (first) network-layer address of the device
  173. * > as reported in the Address TLV of the most recently received
  174. * > CDP message. For example, if the corresponding instance of
  175. * > cacheAddressType had the value 'ip(1)', then this object
  176. * > would be an IPv4-address. If the neighbor device is
  177. * > SNMP-manageable, it is supposed to generate its CDP messages
  178. * > such that this address is one at which it will receive SNMP
  179. * > messages. Use cdpCtAddressTable to extract the remaining
  180. * > addresses from the Address TLV received most recently."
  181. *
  182. * @return array The device's CDP neighbour addresses indexed by the current device's port ID
  183. */
  184. public function neighbourAddresses()
  185. {
  186. $addresses = $this->getSNMP()->subOidWalk( self::OID_CDP_CACHE_NEIGHBOUR_ADDRESS, 15 );
  187. foreach( $addresses as $portId => $address )
  188. {
  189. if( strlen( $address ) == 8 && $this->neighbourAddressTypes()[ $portId ] == self::CDP_CACHE_NEIGHBOUR_ADDRESS_TYPE_IP )
  190. $addresses[ $portId ] = long2ip( hexdec( $address ) );
  191. }
  192. return $addresses;
  193. }
  194. /**
  195. * Get the device's CDP neighbour version indexed by the current device's port ID
  196. *
  197. * > The Version string as reported in the most recent CDP
  198. * > message. The zero-length string indicates no Version
  199. * > field (TLV) was reported in the most recent CDP message."
  200. *
  201. * @return array The device's CDP neighbour version indexed by the current device's port ID
  202. */
  203. public function neighbourVersions()
  204. {
  205. return $this->getSNMP()->subOidWalk( self::OID_CDP_CACHE_NEIGHBOUR_VERSION, 15 );
  206. }
  207. /**
  208. * Get the device's CDP neighbours (by their CDP ID) indexed by the current device's port ID
  209. *
  210. * > The Device-ID string as reported in the most recent CDP
  211. * > message. The zero-length string indicates no Device-ID
  212. * > field (TLV) was reported in the most recent CDP message."
  213. *
  214. * @return array The device's CDP neighbours (by their CDP ID) indexed by the current device's port ID
  215. */
  216. public function neighbourId()
  217. {
  218. return $this->getSNMP()->subOidWalk( self::OID_CDP_CACHE_NEIGHBOUR_ID, 15 );
  219. }
  220. /**
  221. * Get the device's CDP neighbours connected port description indexed by the current device's port ID
  222. *
  223. * E.g. a sample call may return:
  224. *
  225. * Array
  226. * (
  227. * [10101] => GigabitEthernet0/1
  228. * [10102] => FastEthernet0/2
  229. * [10103] => GigabitEthernet1/0/24
  230. * [10105] => GigabitEthernet1/0/2
  231. * )
  232. *
  233. * meaning, for example, that our local port with ID `10101` is connected to port `GigabitEthernet0/1` on the neighbour
  234. * connected to that local port. You can discover the neighbour ID via `neighbourId()`.
  235. *
  236. * > The Port-ID string as reported in the most recent CDP
  237. * > message. This will typically be the value of the ifName
  238. * > object (e.g., 'Ethernet0'). The zero-length string
  239. * > indicates no Port-ID field (TLV) was reported in the
  240. * > most recent CDP message.
  241. *
  242. * @see \OSS_SNMP\SNMP\MIBS\Cisco\CDP::neighbourId()
  243. * @return array The device's CDP neighbours connected port *description* indexed by the current device's port ID
  244. */
  245. public function neighbourPort()
  246. {
  247. return $this->getSNMP()->subOidWalk( self::OID_CDP_CACHE_NEIGHBOUR_PORT, 15 );
  248. }
  249. /**
  250. * Get the device's CDP neighbour platforms indexed by the current device's port ID
  251. *
  252. * > The Device's Hardware Platform as reported in the most recent CDP
  253. * > message. The zero-length string indicates that no Platform field
  254. * > (TLV) was reported in the most recent CDP message.
  255. *
  256. * @return array The device's CDP neighbour platforms indexed by the current device's port ID
  257. */
  258. public function neighbourPlatforms()
  259. {
  260. return $this->getSNMP()->subOidWalk( self::OID_CDP_CACHE_NEIGHBOUR_PLATFORM, 15 );
  261. }
  262. /**
  263. * Constant for possible value of CDP neighbour capability
  264. * @see neighbourCapability()
  265. */
  266. const CDP_CACHE_NEIGHBOUR_CAPABILITY_ROUTER = 0b1;
  267. /**
  268. * Constant for possible value of CDP neighbour capability
  269. * @see neighbourCapability()
  270. */
  271. const CDP_CACHE_NEIGHBOUR_CAPABILITY_TRANSPARENT_BRIDGE = 0b10;
  272. /**
  273. * Constant for possible value of CDP neighbour capability
  274. * @see neighbourCapability()
  275. */
  276. const CDP_CACHE_NEIGHBOUR_CAPABILITY_SOURCE_ROUTE_BRIDGE = 0b100;
  277. /**
  278. * Constant for possible value of CDP neighbour capability
  279. * @see neighbourCapability()
  280. */
  281. const CDP_CACHE_NEIGHBOUR_CAPABILITY_SWITCH = 0b1000;
  282. /**
  283. * Constant for possible value of CDP neighbour capability
  284. * @see neighbourCapability()
  285. */
  286. const CDP_CACHE_NEIGHBOUR_CAPABILITY_HOST = 0b10000;
  287. /**
  288. * Constant for possible value of CDP neighbour capability
  289. * @see neighbourCapability()
  290. */
  291. const CDP_CACHE_NEIGHBOUR_CAPABILITY_IGMP_CAPABLE = 0b100000;
  292. /**
  293. * Constant for possible value of CDP neighbour capability
  294. * @see neighbourCapability()
  295. */
  296. const CDP_CACHE_NEIGHBOUR_CAPABILITY_REPEATER = 0b1000000;
  297. /**
  298. * Text representation of CDP capabilities
  299. *
  300. * @see neighbourCapability()
  301. * @var array Text representation of CDP neighbour capabilities
  302. */
  303. public static $CDP_CACHE_NEIGHBOUR_CAPABILITIES = array(
  304. self::CDP_CACHE_NEIGHBOUR_CAPABILITY_ROUTER => 'Router',
  305. self::CDP_CACHE_NEIGHBOUR_CAPABILITY_TRANSPARENT_BRIDGE => 'Transparent Bridge',
  306. self::CDP_CACHE_NEIGHBOUR_CAPABILITY_SOURCE_ROUTE_BRIDGE => 'Source Route Bridge',
  307. self::CDP_CACHE_NEIGHBOUR_CAPABILITY_SWITCH => 'Switch',
  308. self::CDP_CACHE_NEIGHBOUR_CAPABILITY_HOST => 'Host',
  309. self::CDP_CACHE_NEIGHBOUR_CAPABILITY_IGMP_CAPABLE => 'IGMP Capable',
  310. self::CDP_CACHE_NEIGHBOUR_CAPABILITY_REPEATER => 'Repeater'
  311. );
  312. /**
  313. * Get the device's CDP neighbour capabilities (as a decimal integer) indexed by the current device's port ID
  314. *
  315. * > The Device's Functional Capabilities as reported in the most
  316. * > recent CDP message. For latest set of specific values, see
  317. * > the latest version of the CDP specification. The zero-length
  318. * > string indicates no Capabilities field (TLV) was reported in
  319. * > the most recent CDP message."
  320. *
  321. * @see REFERENCE "Cisco Discovery Protocol Specification, 10/19/94."
  322. * @see http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#xtocid12
  323. * @see http://wiki.wireshark.org/CDP
  324. *
  325. * @return array The device's CDP neighbour capabilities (as a decimal integer) indexed by the current device's port ID
  326. */
  327. public function neighbourCapability()
  328. {
  329. $rtn = $this->getSNMP()->subOidWalk( self::OID_CDP_CACHE_NEIGHBOUR_CAPABILITY, 15 );
  330. foreach( $rtn as $k => $v )
  331. $rtn[$k] = (int)hexdec( $v );
  332. return $rtn;
  333. }
  334. /**
  335. * Query if a given neighbour (by connected port ID) has the given capability
  336. *
  337. * Example:
  338. *
  339. * if( $host->useCisco_CDP()->neighbourHasCapability( $portId, \OSS_SNMP\SNMP\MIBS\Cisco\CDP::CDP_CACHE_NEIGHBOUR_CAPABILITY_SWITCH )
  340. * echo "Host is a switch!!";
  341. *
  342. * @param int $portId The CDP neighbour by connected local port ID
  343. * @param int $capability The capability to query for (defined by self::CDP_CACHE_NEIGHBOUR_CAPABILITY_XXX constants)
  344. * @return boolean True if the neighbour has the given capability
  345. */
  346. public function neighbourHasCapability( $portId, $capability )
  347. {
  348. if( $this->neighbourCapability()[ $portId ] & $capability )
  349. return true;
  350. return false;
  351. }
  352. /**
  353. * Get an array of individual capabilities of a given neighbour (by connected port ID)
  354. *
  355. * Example:
  356. *
  357. * print_r( $host->useCisco_CDP()->neighbourCapabilities( 10111 ) )
  358. *
  359. * [0] => 8 // self::CDP_CACHE_NEIGHBOUR_CAPABILITY_SWITCH
  360. * [1] => 32 // self::CDP_CACHE_NEIGHBOUR_CAPABILITY_IGMP_CAPABLE
  361. *
  362. * print_r( $host->useCisco_CDP()->neighbourCapabilities( 10111, true ) )
  363. *
  364. * [0] => "Switch" // self::CDP_CACHE_NEIGHBOUR_CAPABILITY_SWITCH
  365. * [1] => "IGMP Capable" // self::CDP_CACHE_NEIGHBOUR_CAPABILITY_IGMP_CAPABLE
  366. *
  367. *
  368. *
  369. * @param int $portId The CDP neighbour by connected local port ID
  370. * @param int $translate Set to true to return descriptions rather than integers
  371. * @return array Individual capabilities of a given neighbour
  372. */
  373. public function neighbourCapabilities( $portId, $translate = false )
  374. {
  375. $capabilities = array();
  376. foreach( self::$CDP_CACHE_NEIGHBOUR_CAPABILITIES as $mask => $description )
  377. {
  378. if( $this->neighbourCapability()[ $portId ] & $mask )
  379. $capabilities[] = $mask;
  380. }
  381. if( $translate )
  382. return $this->getSNMP()->translate( $capabilities, self::$CDP_CACHE_NEIGHBOUR_CAPABILITIES );
  383. return $capabilities;
  384. }
  385. /**
  386. * Get the device's CDP neighbours' VTP management domain indexed by the current device's port ID
  387. *
  388. * > The VTP Management Domain for the remote device's interface,
  389. * > as reported in the most recently received CDP message. This
  390. * > object is not instantiated if no VTP Management Domain field
  391. * > (TLV) was reported in the most recently received CDP message.
  392. *
  393. * @see REFERENCE "managementDomainName in CISCO-VTP-MIB"
  394. *
  395. * @return array The device's CDP neighbours' VTP management domain indexed by the current device's port ID
  396. */
  397. public function neighbourVTPMgmtDomain()
  398. {
  399. return $this->getSNMP()->subOidWalk( self::OID_CDP_CACHE_NEIGHBOUR_VTP_MGMT_DOMAIN, 15 );
  400. }
  401. /**
  402. * Get the remote device's interface's native VLAN (indexed by local portId)
  403. *
  404. * > The remote device's interface's native VLAN, as reported in the
  405. * > most recent CDP message. The value 0 indicates
  406. * > no native VLAN field (TLV) was reported in the most
  407. * > recent CDP message.
  408. *
  409. * @return array The remote device's interface's native VLAN (indexed by local portId)
  410. */
  411. public function neighbourNativeVLAN()
  412. {
  413. return $this->getSNMP()->subOidWalk( self::OID_CDP_CACHE_NEIGHBOUR_NATIVE_VLAN, 15 );
  414. }
  415. /**
  416. * Constant for possible value of CDP neighbour duplex
  417. * @see neighbourDuplexMode()
  418. */
  419. const CDP_CACHE_NEIGHBOUR_DUPLEX_UNKNOWN = 1;
  420. /**
  421. * Constant for possible value of CDP neighbour duplex
  422. * @see neighbourDuplexMode()
  423. */
  424. const CDP_CACHE_NEIGHBOUR_DUPLEX_HALF = 2;
  425. /**
  426. * Constant for possible value of CDP neighbour duplex
  427. * @see neighbourDuplexMode()
  428. */
  429. const CDP_CACHE_NEIGHBOUR_DUPLEX_FULL = 3;
  430. /**
  431. * Text representation of CDP capabilities
  432. *
  433. * @see neighbourDuplexMode()
  434. * @var array Text representation of CDP neighbour duplex modes
  435. */
  436. public static $CDP_CACHE_NEIGHBOUR_DUPLEXES = array(
  437. self::CDP_CACHE_NEIGHBOUR_DUPLEX_UNKNOWN => 'unknown',
  438. self::CDP_CACHE_NEIGHBOUR_DUPLEX_HALF => 'half-duplex',
  439. self::CDP_CACHE_NEIGHBOUR_DUPLEX_FULL => 'full-duplex'
  440. );
  441. /**
  442. * Get the remote device's interface's duplex mode (indexed by local portId)
  443. *
  444. * > he remote device's interface's duplex mode, as reported in the
  445. * > most recent CDP message. The value unknown(1) indicates
  446. * > no duplex mode field (TLV) was reported in the most
  447. * > recent CDP message."
  448. *
  449. * @param boolean $translate If true, return the string representation via self::$VTP_VLAN_TYPES
  450. * @return array The remote device's interface's duplex mode (indexed by local portId)
  451. */
  452. public function neighbourDuplexMode( $translate = false )
  453. {
  454. $modes = $this->getSNMP()->subOidWalk( self::OID_CDP_CACHE_NEIGHBOUR_DUPLEX, 15 );
  455. if( !$translate )
  456. return $modes;
  457. return $this->getSNMP()->translate( $modes, self::$CDP_CACHE_NEIGHBOUR_DUPLEXES );
  458. }
  459. /**
  460. * Get the remote device's last change time (indexed by local portId)
  461. *
  462. * > Indicates the time when this cache entry was last changed.
  463. * > This object is initialised to the current time when the entry
  464. * > gets created and updated to the current time whenever the value
  465. * > of any (other) object instance in the corresponding row is
  466. * > modified."
  467. *
  468. * @return array The remote device's last change time(indexed by local portId)
  469. */
  470. public function neighbourLastChange()
  471. {
  472. return $this->getSNMP()->subOidWalk( self::OID_CDP_CACHE_NEIGHBOUR_LAST_CHANGE, 15 );
  473. }
  474. /**
  475. * CDP utility function to get all CDP neighbours and their connected ports.
  476. *
  477. * Returns an array of neighbours indexed by the neighbour CDP ID with a lot of details.
  478. *
  479. * For example, here's a sample return for a switch with two neighbours where one neighbour
  480. * is connected with a LAG / PortChannel and `$inverse` was set to true.
  481. *
  482. *
  483. * array(2) {
  484. * ["cr-sw07.example.ie"] => array(1) {
  485. * [0] => array(7) {
  486. * ["localPortId"] => int(10103)
  487. * ["localPortName"] => string(7) "Gi1/0/3"
  488. * ["localPort"] => string(20) "GigabitEthernet1/0/3"
  489. * ["isLAG"] => bool(false)
  490. * ["remotePort"] => string(21) "GigabitEthernet1/0/24"
  491. * ["remotePortId"] => int(10124)
  492. * ["remotePortName"] => string(8) "Gi1/0/24"
  493. * }
  494. * }
  495. * ["cr-sw01.example.ie"] => array(2) {
  496. * [0] => array(11) {
  497. * ["localPortId"] => int(10111)
  498. * ["localPortName"] => string(8) "Gi1/0/11"
  499. * ["localPort"] => string(21) "GigabitEthernet1/0/11"
  500. * ["isLAG"] => bool(true)
  501. * ["lagPortId"] => int(5048)
  502. * ["lagPortName"] => string(4) "Po48"
  503. * ["remotePort"] => string(21) "GigabitEthernet1/0/11"
  504. * ["remotePortId"] => int(10111)
  505. * ["remotePortName"] => string(8) "Gi1/0/11"
  506. * ["remoteLagPortId"] => int(5048)
  507. * ["remoteLagPortName"] => string(4) "Po48"
  508. * }
  509. * [1] => array(11) {
  510. * ["localPortId"] => int(10112)
  511. * ["localPortName"] => string(8) "Gi1/0/12"
  512. * ["localPort"] => string(21) "GigabitEthernet1/0/12"
  513. * ["isLAG"] => bool(true)
  514. * ["lagPortId"] => int(5048)
  515. * ["lagPortName"] => string(4) "Po48"
  516. * ["remotePort"] => string(21) "GigabitEthernet1/0/12"
  517. * ["remotePortId"] => int(10112)
  518. * ["remotePortName"] => string(8) "Gi1/0/12"
  519. * ["remoteLagPortId"] => int(5048)
  520. * ["remoteLagPortName"] => string(4) "Po48"
  521. * }
  522. * }
  523. *
  524. * @see neighbourId()
  525. * @see \OSS_SNMP\SNMP\MIBS\Interface::descriptions()
  526. * @see neighbourPort()
  527. * @param boolean $inverse If true, all remoteXXX params will be discovered (only remotePort is returned otherwise)
  528. * @param array $skipHostIds If using $inverse, pass an array of CDP IDs of neighbours that should not be 'inverse' discovered.
  529. * @return array CDP neighbours and their connected ports
  530. */
  531. public function neighbours( $inverse = false, $skipHostIds = null )
  532. {
  533. $neighbours = array();
  534. $remotes = array();
  535. foreach( $this->neighbourId() as $localPortId => $neighbourCdpId )
  536. {
  537. if( !isset( $neighbours[ $neighbourCdpId ] ) )
  538. {
  539. $neighbours[ $neighbourCdpId ] = array();
  540. $count = 0;
  541. }
  542. else
  543. $count = count( $neighbours[ $neighbourCdpId ] );
  544. $neighbours[ $neighbourCdpId ][$count]['localPortId'] = $localPortId;
  545. $neighbours[ $neighbourCdpId ][$count]['localPortName'] = $this->getSNMP()->useIface()->names()[$localPortId];
  546. $neighbours[ $neighbourCdpId ][$count]['localPort'] = $this->getSNMP()->useIface()->descriptions()[$localPortId];
  547. try
  548. {
  549. if( $this->getSNMP()->useLAG()->isAggregatePorts()[$localPortId] )
  550. {
  551. $neighbours[ $neighbourCdpId ][$count]['isLAG'] = true;
  552. $neighbours[ $neighbourCdpId ][$count]['lagPortId'] = $this->getSNMP()->useLAG()->portAttachedIds()[$localPortId];
  553. $neighbours[ $neighbourCdpId ][$count]['lagPortName'] = $this->getSNMP()->useIface()->names()[ $neighbours[ $neighbourCdpId ][$count]['lagPortId'] ];
  554. }
  555. else
  556. {
  557. $neighbours[ $neighbourCdpId ][$count]['isLAG'] = false;
  558. }
  559. }
  560. catch( \OSS_SNMP\Exception $e )
  561. {
  562. $neighbours[ $neighbourCdpId ][$count]['isLAG'] = false;
  563. }
  564. $neighbours[ $neighbourCdpId ][$count]['remotePort'] = $this->neighbourPort()[$localPortId];
  565. if( $inverse )
  566. {
  567. if( !is_array( $skipHostIds ) || !in_array( $neighbourCdpId, $skipHostIds ) )
  568. {
  569. try
  570. {
  571. if( !isset( $remotes[ $neighbourCdpId ] ) )
  572. $remotes[ $neighbourCdpId ] = new \OSS_SNMP\SNMP( $neighbourCdpId, $this->getSNMP()->getCommunity() );
  573. $remote = $remotes[ $neighbourCdpId ];
  574. $rneighbours = $remote->useCisco_CDP()->neighbours( false );
  575. foreach( $rneighbours as $rNeighbourCdpId => $rLinks )
  576. {
  577. foreach( $rLinks as $rIndex => $rPortDetails )
  578. {
  579. $rLocalPortId = $rPortDetails[ 'localPortId' ];
  580. if( $rNeighbourCdpId == $this->id()
  581. && $remote->useIface()->descriptions()[ $rLocalPortId ] == $neighbours[ $neighbourCdpId ][$count]['remotePort'] )
  582. {
  583. $neighbours[ $neighbourCdpId ][$count]['remotePortId'] = $rLocalPortId;
  584. $neighbours[ $neighbourCdpId ][$count]['remotePortName'] = $remote->useIface()->names()[ $rLocalPortId ];
  585. if( $neighbours[ $neighbourCdpId ][$count]['isLAG'] )
  586. {
  587. $neighbours[ $neighbourCdpId ][$count]['remoteLagPortId'] = $remote->useLAG()->portAttachedIds()[$rLocalPortId];
  588. $neighbours[ $neighbourCdpId ][$count]['remoteLagPortName'] = $remote->useIface()->names()[ $neighbours[ $neighbourCdpId ][$count]['remoteLagPortId'] ];
  589. }
  590. break;
  591. }
  592. }
  593. }
  594. }
  595. catch( \OSS_SNMP\Exception $e )
  596. {
  597. }
  598. }
  599. }
  600. }
  601. return $neighbours;
  602. }
  603. /**
  604. * Recursivily crawls all CDP neighbours to build up a flat array of all devices
  605. * indexed by the CDP device id.
  606. *
  607. * Array form is same as that returned by neighbours()
  608. *
  609. * @see neighbours()
  610. * @param array $devices Unless you're doing something funky, just pass an empty array. This is where the result will be found.
  611. * @param string $device CDP device ID of next host to crawl. On first pass, set to null - used internally when recursing
  612. * @param array $ignore An array of CDP device IDs to *ignore*. I.e. to not include in recursive crawling
  613. * @return array The resultant array of all crawled devices (same as that passed in the @param $devices parameter)
  614. */
  615. public function crawl( &$devices = array(), $device = null, $ignore = array() )
  616. {
  617. if( !count( $devices ) )
  618. {
  619. $device = $this->id();
  620. $devices[ $device ] = $this->neighbours( true, $ignore );
  621. }
  622. foreach( $devices[ $device ] as $feNeighbour => $feConnections )
  623. {
  624. if( in_array( $feNeighbour, $ignore ) )
  625. {
  626. if( isset( $devices[ $device ][$feNeighbour] ) )
  627. unset( $devices[ $device ][$feNeighbour] );
  628. continue;
  629. }
  630. if( !isset( $devices[ $feNeighbour ] ) )
  631. {
  632. $snmp = new \OSS_SNMP\SNMP( $feNeighbour, $this->getSNMP()->getCommunity() );
  633. try
  634. {
  635. $devices[ $feNeighbour ] = $snmp->useCisco_CDP()->neighbours( true, $ignore );
  636. unset( $snmp );
  637. $this->crawl( $devices, $feNeighbour, $ignore );
  638. }
  639. catch( \OSS_SNMP\Exception $e )
  640. {
  641. // this device did not respond / have CDP enabled / CDP available - skip
  642. unset( $devices[$feNeighbour] );
  643. }
  644. }
  645. }
  646. return $devices;
  647. }
  648. /**
  649. * Find the layer 2 topology as an array with no link mentioned more than once.
  650. *
  651. * Huh? This function:
  652. *
  653. * * takes the result of crawl() (or calls crawl()) to get the CDP topology;
  654. * * foreach device, builds an array of device to device links;
  655. * * SO LONG as that link has already not been accounted for in the other direction.
  656. *
  657. * I.e. if a link is found A -> B, then the same B -> A link will not be included.
  658. *
  659. * The primary differences to the return value of this and crawl() are:
  660. *
  661. * * links only appear once (unidirectional) rather than twice (bidirectional)
  662. * * the links are indexed by the localPortName rather than an integer index:
  663. *
  664. * [cr-sw04.degkcp.example.ie] => Array
  665. * (
  666. * [cd-sw02.degkcp.example.ie] => Array
  667. * (
  668. * [GigabitEthernet1/0/3] => Array
  669. * (
  670. * [remotePort] => FastEthernet0/1
  671. * [isLAG] => false
  672. * ........
  673. * )
  674. * )
  675. *
  676. *
  677. * All port information is copied over from the supplied / called `crawl()` array
  678. *
  679. * @see crawl()
  680. * @param array $devices The result of crawl() (if null, this function performs a crawl())
  681. * @return array L2 topology as described above.
  682. */
  683. public function linkTopology( $devices = null )
  684. {
  685. if( $devices == null )
  686. $devices = $this->crawl();
  687. $links = array();
  688. foreach( $devices as $feDevice => $feNeighbours )
  689. {
  690. foreach( $feNeighbours as $fe2Device => $fe2Links )
  691. {
  692. foreach( $fe2Links as $fe2Link )
  693. {
  694. // have we already accounted for this link on the other side?
  695. if( isset( $links[ $fe2Device ][ $feDevice ][ $fe2Link['remotePort'] ] ) )
  696. continue;
  697. if( !isset( $links[ $feDevice ] ) )
  698. $links[ $feDevice ] = array();
  699. if( !isset( $links[ $feDevice ][ $fe2Device ] ) )
  700. $links[ $feDevice ][ $fe2Device ] = array();
  701. $links[ $feDevice ][ $fe2Device ][ $fe2Link['localPort'] ] = array();
  702. foreach( $fe2Link as $k => $v )
  703. $links[ $feDevice ][ $fe2Device ][ $fe2Link['localPort'] ][ $k ] = $v;
  704. }
  705. }
  706. }
  707. return $links;
  708. }
  709. /**
  710. * Utility function to process the output from neighbours() and remove individual trunk ports leaving only
  711. * single LAG links.
  712. *
  713. * For example, here's a sample return for a switch with a neighbour
  714. * connected with a LAG / PortChannel:
  715. *
  716. * array(2) {
  717. * ["cr-sw01.example.ie"] => array(2) {
  718. * [0] => array(11) {
  719. * ["localPortId"] => int(10111)
  720. * ["localPortName"] => string(8) "Gi1/0/11"
  721. * ["localPort"] => string(21) "GigabitEthernet1/0/11"
  722. * ["isLAG"] => bool(true)
  723. * ["lagPortId"] => int(5048)
  724. * ["lagPortName"] => string(4) "Po48"
  725. * ["remotePort"] => string(21) "GigabitEthernet1/0/11"
  726. * ["remotePortId"] => int(10111)
  727. * ["remotePortName"] => string(8) "Gi1/0/11"
  728. * ["remoteLagPortId"] => int(5048)
  729. * ["remoteLagPortName"] => string(4) "Po48"
  730. * }
  731. * [1] => array(11) {
  732. * ["localPortId"] => int(10112)
  733. * ["localPortName"] => string(8) "Gi1/0/12"
  734. * ["localPort"] => string(21) "GigabitEthernet1/0/12"
  735. * ["isLAG"] => bool(true)
  736. * ["lagPortId"] => int(5048)
  737. * ["lagPortName"] => string(4) "Po48"
  738. * ["remotePort"] => string(21) "GigabitEthernet1/0/12"
  739. * ["remotePortId"] => int(10112)
  740. * ["remotePortName"] => string(8) "Gi1/0/12"
  741. * ["remoteLagPortId"] => int(5048)
  742. * ["remoteLagPortName"] => string(4) "Po48"
  743. * }
  744. * }
  745. * ...
  746. * }
  747. *
  748. * The result of this function would be:
  749. *
  750. * array(2) {
  751. * ["cr-sw01.example.ie"] => array(1) {
  752. * [0] => array(11) {
  753. * ["localPortId"] => int(5048)
  754. * ["localPortName"] => string(8) "Po48"
  755. * ["localPort"] => string(21) "Po48"
  756. * ["isLAG"] => bool(true)
  757. * ["lagPortId"] => int(5048)
  758. * ["lagPortName"] => string(4) "Po48"
  759. * ["remotePort"] => string(21) "Po48"
  760. * ["remotePortId"] => int(5048)
  761. * ["remotePortName"] => string(8) "Po48"
  762. * ["remoteLagPortId"] => int(5048)
  763. * ["remoteLagPortName"] => string(4) "Po48"
  764. * }
  765. * }
  766. * ...
  767. * }
  768. *
  769. * @see neighbours()
  770. * @param array $neighbours The result of a call to neighbours()
  771. * @return array Processed CDP neighbours with LAG ports collapsed
  772. */
  773. public function collapseLAGs( $neighbours )
  774. {
  775. foreach( $neighbours as $neighbour => $links )
  776. {
  777. $removed = array();
  778. foreach( $links as $idx => $linkDetails )
  779. {
  780. if( $linkDetails['isLAG'] )
  781. {
  782. if( isset( $removed[ $linkDetails['localLagPortId'] ] ) )
  783. unset( $neighbours[ $neighbour ][ $idx ] );
  784. else
  785. {
  786. $removed[ $linkDetails['localLagPortId'] ] = true;
  787. $neighbours[ $neighbour ][ $idx ]['localPortId'] = $neighbours[ $neighbour ][ $idx ]['lagPortId'];
  788. $neighbours[ $neighbour ][ $idx ]['localPortName'] = $neighbours[ $neighbour ][ $idx ]['lagPortName'];
  789. $neighbours[ $neighbour ][ $idx ]['localPort'] = $neighbours[ $neighbour ][ $idx ]['lagPortName'];
  790. $neighbours[ $neighbour ][ $idx ]['remotePortId'] = $neighbours[ $neighbour ][ $idx ]['remoteLagPortId'];
  791. $neighbours[ $neighbour ][ $idx ]['remotePortName'] = $neighbours[ $neighbour ][ $idx ]['remoteLagPortName'];
  792. $neighbours[ $neighbour ][ $idx ]['remotePort'] = $neighbours[ $neighbour ][ $idx ]['remoteLagPortName'];
  793. }
  794. }
  795. }
  796. }
  797. return $neighbours;
  798. }
  799. /**
  800. * An extension of `collapseLAGs()` to work with `crawl()` and `linkTopology()`.
  801. *
  802. * Rather than taking the input from `neighbours()`, it takes input from `crawl()` or `linkTopology()`
  803. * and processes all neighbours.
  804. *
  805. * @see collapseLAGs()
  806. * @param array $devices The result of a call to crawl() or linkTopology()
  807. * @return array Processed CDP neighbours with LAG ports collapsed
  808. */
  809. public function collapseDevicesLAGs( $devices )
  810. {
  811. foreach( $devices as $parent => $neighbours )
  812. $devices[ $parent ] = $this->collapseLAGs( $neighbours );
  813. return $devices;
  814. }
  815. }