CDP.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  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\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_NEIGHBOUR_ID = '.1.3.6.1.4.1.9.9.23.1.2.1.1.6';
  41. const OID_CDP_NEIGHBOUR_PORT = '.1.3.6.1.4.1.9.9.23.1.2.1.1.7';
  42. const OID_CDP_NEIGHBOUR_CAPABILITY = '.1.3.6.1.4.1.9.9.23.1.2.1.1.9';
  43. const OID_CDP_DEVICE_ID = '.1.3.6.1.4.1.9.9.23.1.3.4.0';
  44. /**
  45. * Get the device's CDP (Cisco Discovery Protocol) ID
  46. *
  47. * @return string The device's CDP (Cisco Discovery Protocol) ID
  48. */
  49. public function id()
  50. {
  51. return $this->getSNMP()->get( self::OID_CDP_DEVICE_ID );
  52. }
  53. /**
  54. * Get the device's interfaces CDP enabled status
  55. *
  56. * Applies the TruthValue post processor (self::ppTruthValue()) to turn
  57. * SNMP values into true / false.
  58. *
  59. * @return array The device's interfaces CDP enabled status' (as boolean true / false)
  60. */
  61. public function interfaceEnabled()
  62. {
  63. return $this->getSNMP()->ppTruthValue( $this->getSNMP()->walk1d( self::OID_CDP_INTERFACE_ENABLED ) );
  64. }
  65. /**
  66. * Get the device's interface names as seen in CDP
  67. *
  68. * @return array The device's interface names as seen in CDP
  69. */
  70. public function interfaceNames()
  71. {
  72. return $this->getSNMP()->walk1d( self::OID_CDP_INTERFACE_NAME );
  73. }
  74. /**
  75. * Get the device's CDP neighbours (by their CDP ID) indexed by the current device's port ID
  76. *
  77. * @return array The device's CDP neighbours (by their CDP ID) indexed by the current device's port ID
  78. */
  79. public function neighbourId()
  80. {
  81. return $this->getSNMP()->subOidWalk( self::OID_CDP_NEIGHBOUR_ID, 15 );
  82. }
  83. /**
  84. * Get the device's CDP neighbours connected port *description* indexed by the current device's port ID
  85. *
  86. * E.g. a sample call may return:
  87. *
  88. * Array
  89. * (
  90. * [10101] => GigabitEthernet0/1
  91. * [10102] => FastEthernet0/2
  92. * [10103] => GigabitEthernet1/0/24
  93. * [10105] => GigabitEthernet1/0/2
  94. * )
  95. *
  96. * meaning, for example, that our local port with ID 10101 is connected to port GigabitEthernet0/1 on the neighbour
  97. * connected to that local port. You can discover the neighbour ID via neighbourId().
  98. *
  99. * @see neighbourId()
  100. * @return array The device's CDP neighbours connected port *description* indexed by the current device's port ID
  101. */
  102. public function neighbourPort()
  103. {
  104. return $this->getSNMP()->subOidWalk( self::OID_CDP_NEIGHBOUR_PORT, 15 );
  105. }
  106. /**
  107. * Get the device's CDP neighbour capabilities (as a decimal integer) indexed by the current device's port ID
  108. *
  109. * @return array The device's CDP neighbour capabilities (as a decimal integer) indexed by the current device's port ID
  110. */
  111. public function neighbourCapability()
  112. {
  113. $rtn = $this->getSNMP()->subOidWalk( self::OID_CDP_NEIGHBOUR_CAPABILITY, 15 );
  114. foreach( $rtn as $k => $v )
  115. $rtn[$k] = (int)hexdec( $v );
  116. return $rtn;
  117. }
  118. /**
  119. * CDP utility function to get all CDP neighbours and their connected ports.
  120. *
  121. * Returns an array of neighbours indexed by the neighbour CDP ID. For example:
  122. *
  123. *
  124. * Array
  125. * (
  126. * [cr-sw03.ixdub1.opensolutions.ie] => Array
  127. * (
  128. * [0] => Array
  129. * (
  130. * [localPortId] => 10101
  131. * [localPort] => GigabitEthernet1/0/1
  132. * [remotePort] => GigabitEthernet0/1
  133. * )
  134. *
  135. * [1] => Array
  136. * (
  137. * [localPortId] => 10102
  138. * [localPort] => GigabitEthernet1/0/2
  139. * [remotePort] => FastEthernet0/2
  140. * )
  141. *
  142. * )
  143. * [ ... ]
  144. * )
  145. *
  146. * @see neighbourId()
  147. * @see \OSS\SNMP\MIBS\Interface::descriptions()
  148. * @see neighbourPort()
  149. * @return array CDP neighbours and their connected ports
  150. */
  151. public function neighbours()
  152. {
  153. $neighbours = array();
  154. foreach( $this->neighbourId() as $localPortId => $neighbourCdpId )
  155. {
  156. if( !isset( $neighbours[ $neighbourCdpId ] ) )
  157. {
  158. $neighbours[ $neighbourCdpId ] = array();
  159. $count = 0;
  160. }
  161. else
  162. $count = count( $neighbours[ $neighbourCdpId ] );
  163. $neighbours[ $neighbourCdpId ][$count]['localPortId'] = $localPortId;
  164. $neighbours[ $neighbourCdpId ][$count]['localPortName'] = $this->getSNMP()->useIface()->names()[$localPortId];
  165. $neighbours[ $neighbourCdpId ][$count]['localPort'] = $this->getSNMP()->useIface()->descriptions()[$localPortId];
  166. $neighbours[ $neighbourCdpId ][$count]['isLAG'] = $this->getSNMP()->useLAG()->isAggregatePorts()[$localPortId];
  167. $neighbours[ $neighbourCdpId ][$count]['remotePort'] = $this->neighbourPort()[$localPortId];
  168. }
  169. return $neighbours;
  170. }
  171. /**
  172. * Recursivily crawls all CDP neighbours to build up a flat array of all devices
  173. * indexed by the CDP device id.
  174. *
  175. * Array form is same as that returned by neighbours()
  176. *
  177. * @see neighbours()
  178. * @param array $devices Unless you're doing something funky, just pass an empty array. This is where the result will be found.
  179. * @param string $device CDP device ID of next host to crawl. On first pass, set to null - used internally when recursing
  180. * @param array $ignore An array of CDP device IDs to *ignore*. I.e. to not include in recursive crawling
  181. * @return array The resultant array of all crawled devices (same as that passed in the @param $devices parameter)
  182. */
  183. public function crawl( &$devices = array(), $device = null, $ignore = array() )
  184. {
  185. if( !count( $devices ) )
  186. {
  187. $device = $this->id();
  188. $devices[ $device ] = $this->neighbours();
  189. }
  190. foreach( $devices[ $device ] as $feNeighbour => $feConnections )
  191. {
  192. if( in_array( $feNeighbour, $ignore ) )
  193. {
  194. if( isset( $devices[ $device ][$feNeighbour] ) )
  195. unset( $devices[ $device ][$feNeighbour] );
  196. continue;
  197. }
  198. if( !isset( $devices[ $feNeighbour ] ) )
  199. {
  200. $snmp = new \OSS\SNMP( $feNeighbour, $this->getSNMP()->getCommunity() );
  201. $devices[ $feNeighbour ] = $snmp->useCisco_CDP()->neighbours();
  202. unset( $snmp );
  203. $this->crawl( $devices, $feNeighbour, $ignore );
  204. }
  205. }
  206. return $devices;
  207. }
  208. /**
  209. * Find the layer 2 topology as an array with no link mentioned more than once.
  210. *
  211. * Huh? This function:
  212. *
  213. * * takes the result of crawl() (or calls crawl()) to get the CDP topology
  214. * * foreach device, builds an array of device to device links
  215. * * SO LONG as that link has already not been accounted for in the other direction.
  216. *
  217. * I.e. if a link is found A -> B, then the same B -> A link will not be included
  218. *
  219. * The array returned is, for example:
  220. *
  221. * [cr-sw04.degkcp.example.ie] => Array
  222. * (
  223. * [cd-sw02.degkcp.example.ie] => Array
  224. * (
  225. * [GigabitEthernet1/0/3] => Array
  226. * (
  227. * [remotePort] => FastEthernet0/1
  228. * [isLAG] => false
  229. * )
  230. *
  231. * [cr-sw03.degkcp.example.ie] => Array
  232. * (
  233. * [GigabitEthernet1/0/23] => Array
  234. * (
  235. * [remotePort] => GigabitEthernet1/0/23
  236. * [isLAG] => false
  237. * )
  238. * [GigabitEthernet1/0/24] => Array
  239. * (
  240. * [remotePort] => GigabitEthernet1/0/24
  241. * [isLAG] => false
  242. * )
  243. * )
  244. * )
  245. *
  246. * This tells us that cr-sw04(GigabitEthernet1/0/3) is connected to cd-sw02(FastEthernet0/1).
  247. *
  248. * It also tells us that cr-sw04 has two connections to cr-sw03.
  249. *
  250. * You'll notice it also tells us if it's a LAG or not. More information can be added as needed.
  251. *
  252. * @see crawl()
  253. * @param array $devices The result of crawl() (if null, this function performs a crawl())
  254. * @return array L2 topology as described above.
  255. */
  256. public function linkTopology( $devices = null )
  257. {
  258. if( $devices == null )
  259. $devices = $this->crawl();
  260. $links = array();
  261. foreach( $devices as $feDevice => $feNeighbours )
  262. {
  263. foreach( $feNeighbours as $fe2Device => $fe2Links )
  264. {
  265. foreach( $fe2Links as $fe2Link )
  266. {
  267. // have we already accounted for this link on the other side?
  268. if( isset( $links[ $fe2Device ][ $feDevice ][ $fe2Link['remotePort'] ] ) )
  269. continue;
  270. if( !isset( $links[ $feDevice ] ) )
  271. $links[ $feDevice ] = array();
  272. if( !isset( $links[ $feDevice ][ $fe2Device ] ) )
  273. $links[ $feDevice ][ $fe2Device ] = array();
  274. $links[ $feDevice ][ $fe2Device ][ $fe2Link['localPort'] ] = array();
  275. $links[ $feDevice ][ $fe2Device ][ $fe2Link['localPort'] ][ 'remotePort' ] = $fe2Link['remotePort'];
  276. $links[ $feDevice ][ $fe2Device ][ $fe2Link['localPort'] ][ 'isLAG' ] = $fe2Link['isLAG'];
  277. }
  278. }
  279. }
  280. return $links;
  281. }
  282. }