2016-01-03 21:10:35 +01:00
< ? php
/*
* LibreNMS
*
* Copyright ( c ) 2015 Vitali Kari < vitali . kari @ gmail . com >
*
* This program is free software : you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation , either version 3 of the License , or ( at your
* option ) any later version . Please see LICENSE . txt at the top level of
* the source code distribution for details .
*
* Based on IEEE - 802.1 D - 2004 , ( STP , RSTP )
* needs RSTP - MIB
*/
// Pre-cache existing state of STP for this device from database
$stp_db = dbFetchRow ( 'SELECT * FROM `stp` WHERE `device_id` = ?' , array ( $device [ 'device_id' ]));
//d_echo($stp_db);
$stpprotocol = snmp_get ( $device , 'dot1dStpProtocolSpecification.0' , '-Oqv' , 'RSTP-MIB' );
// FIXME I don't know what "unknown" means, perhaps MSTP? (saw it on some cisco devices)
// But we can try to retrieve data
if ( $stpprotocol == 'ieee8021d' || $stpprotocol == 'unknown' ) {
// set time multiplier to convert from centiseconds to seconds
// all time values are stored in databese as seconds
$tm = '0.01' ;
// some vendors like PBN dont follow the 802.1D implementation and use seconds in SNMP
if ( $device [ 'os' ] == 'pbn' ) {
preg_match ( '/^.* Build (?<build>\d+)/' , $device [ 'version' ], $version );
if ( $version [ build ] <= 16607 ) { // Buggy version :-(
$tm = '1' ;
}
}
// read the 802.1D subtree
$stp_raw = snmpwalk_cache_oid ( $device , 'dot1dStp' , array (), 'RSTP-MIB' );
$stp = array (
'protocolSpecification' => $stp_raw [ 0 ][ 'dot1dStpProtocolSpecification' ],
'priority' => $stp_raw [ 0 ][ 'dot1dStpPriority' ],
'topChanges' => $stp_raw [ 0 ][ 'dot1dStpTopChanges' ],
'rootCost' => $stp_raw [ 0 ][ 'dot1dStpRootCost' ],
'rootPort' => $stp_raw [ 0 ][ 'dot1dStpRootPort' ],
'maxAge' => $stp_raw [ 0 ][ 'dot1dStpMaxAge' ] * $tm ,
'helloTime' => $stp_raw [ 0 ][ 'dot1dStpHelloTime' ] * $tm ,
'holdTime' => $stp_raw [ 0 ][ 'dot1dStpHoldTime' ] * $tm ,
'forwardDelay' => $stp_raw [ 0 ][ 'dot1dStpForwardDelay' ] * $tm ,
'bridgeMaxAge' => $stp_raw [ 0 ][ 'dot1dStpBridgeMaxAge' ] * $tm ,
'bridgeHelloTime' => $stp_raw [ 0 ][ 'dot1dStpBridgeHelloTime' ] * $tm ,
'bridgeForwardDelay' => $stp_raw [ 0 ][ 'dot1dStpBridgeForwardDelay' ] * $tm
);
// set device binding
$stp [ 'device_id' ] = $device [ 'device_id' ];
// read the 802.1D bridge address and set as MAC in database
$mac_raw = snmp_get ( $device , 'dot1dBaseBridgeAddress.0' , '-Oqv' , 'RSTP-MIB' );
// read Time as timetics (in hundredths of a seconds) since last topology change and convert to seconds
$time_since_change = snmp_get ( $device , 'dot1dStpTimeSinceTopologyChange.0' , '-Ovt' , 'RSTP-MIB' );
if ( $time_since_change > '100' ) {
$time_since_change = substr ( $time_since_change , 0 , - 2 ); // convert to seconds since change
2016-08-28 12:32:58 -05:00
} else {
2016-01-03 21:10:35 +01:00
$time_since_change = '0' ;
}
$stp [ 'timeSinceTopologyChange' ] = $time_since_change ;
2016-08-28 12:32:58 -05:00
// designated root is stored in format 2 octet bridge priority + MAC address, so we need to normalize it
2016-01-03 21:10:35 +01:00
$dr = str_replace ( array ( ' ' , ':' , '-' ), '' , strtolower ( $stp_raw [ 0 ][ 'dot1dStpDesignatedRoot' ]));
$dr = substr ( $dr , - 12 ); //remove first two octets
$stp [ 'designatedRoot' ] = $dr ;
// normalize the MAC
$mac_array = explode ( ':' , $mac_raw );
2016-08-28 12:32:58 -05:00
foreach ( $mac_array as & $octet ) {
2016-01-03 21:10:35 +01:00
if ( strlen ( $octet ) < 2 ) {
$octet = " 0 " . $octet ; // add suppressed 0
}
}
$stp [ 'bridgeAddress' ] = implode ( $mac_array );
// I'm the boss?
if ( $stp [ 'bridgeAddress' ] == $stp [ 'designatedRoot' ]) {
$stp [ 'rootBridge' ] = '1' ;
2016-08-28 12:32:58 -05:00
} else {
2016-01-03 21:10:35 +01:00
$stp [ 'rootBridge' ] = '0' ;
}
d_echo ( $stp );
if ( $stp_raw [ 0 ][ 'version' ] == '3' ) {
echo " RSTP " ;
2016-08-28 12:32:58 -05:00
} else {
2016-01-03 21:10:35 +01:00
echo " STP " ;
}
if ( ! $stp_db [ 'bridgeAddress' ] && $stp [ 'bridgeAddress' ]) {
2016-08-28 12:32:58 -05:00
dbInsert ( $stp , 'stp' );
2017-02-14 00:32:02 +02:00
log_event ( 'STP added, bridge address: ' . $stp [ 'bridgeAddress' ], $device , 'stp' , 3 );
2016-01-03 21:10:35 +01:00
echo '+' ;
}
if ( $stp_db [ 'bridgeAddress' ] && ! $stp [ 'bridgeAddress' ]) {
2016-08-28 12:32:58 -05:00
dbDelete ( 'stp' , 'device_id = ?' , array ( $device [ 'device_id' ]));
2017-02-14 00:32:02 +02:00
log_event ( 'STP removed' , $device , 'stp' , 4 );
2016-01-03 21:10:35 +01:00
echo '-' ;
}
2016-01-14 16:54:07 +01:00
// STP port related stuff
2016-08-28 12:32:58 -05:00
foreach ( $stp_raw as $port => $value ) {
2016-01-14 16:54:07 +01:00
if ( $port ) { // $stp_raw[0] ist not port related so we skip this one
$stp_port = array (
'priority' => $stp_raw [ $port ][ 'dot1dStpPortPriority' ],
'state' => $stp_raw [ $port ][ 'dot1dStpPortState' ],
'enable' => $stp_raw [ $port ][ 'dot1dStpPortEnable' ],
'pathCost' => $stp_raw [ $port ][ 'dot1dStpPortPathCost' ],
'designatedCost' => $stp_raw [ $port ][ 'dot1dStpPortDesignatedCost' ],
'designatedPort' => $stp_raw [ $port ][ 'dot1dStpPortDesignatedPort' ],
'forwardTransitions' => $stp_raw [ $port ][ 'dot1dStpPortForwardTransitions' ]
);
// set device binding
$stp_port [ 'device_id' ] = $device [ 'device_id' ];
// set port binding
$stp_port [ 'port_id' ] = dbFetchCell ( 'SELECT port_id FROM `ports` WHERE `device_id` = ? AND `ifIndex` = ?' , array ( $device [ 'device_id' ], $stp_raw [ $port ][ 'dot1dStpPort' ]));
$dr = str_replace ( array ( ' ' , ':' , '-' ), '' , strtolower ( $stp_raw [ $port ][ 'dot1dStpPortDesignatedRoot' ]));
$dr = substr ( $dr , - 12 ); //remove first two octets
$stp_port [ 'designatedRoot' ] = $dr ;
$db = str_replace ( array ( ' ' , ':' , '-' ), '' , strtolower ( $stp_raw [ $port ][ 'dot1dStpPortDesignatedBridge' ]));
$db = substr ( $db , - 12 ); //remove first two octets
$stp_port [ 'designatedBridge' ] = $db ;
if ( $device [ 'os' ] == 'pbn' ) {
// It seems that PBN guys don't care about ieee 802.1d :-(
// So try to find the right port with some crazy conversations
$dp_value = dechex ( $stp_port [ 'priority' ]);
$dp_value = $dp_value . '00' ;
$dp_value = hexdec ( $dp_value );
if ( $stp_raw [ $port ][ 'dot1dStpPortDesignatedPort' ]) {
$dp = $stp_raw [ $port ][ 'dot1dStpPortDesignatedPort' ] - $dp_value ;
$stp_port [ 'designatedPort' ] = $dp ;
}
2016-08-28 12:32:58 -05:00
} else {
2016-01-14 16:54:07 +01:00
// Port saved in format priority+port (ieee 802.1d-1998: clause 8.5.5.1)
$dp = substr ( $stp_raw [ $port ][ 'dot1dStpPortDesignatedPort' ], - 2 ); //discard the first octet (priority part)
$stp_port [ 'designatedPort' ] = hexdec ( $dp );
}
d_echo ( $stp_port );
// Write to db
if ( ! dbFetchCell ( 'SELECT 1 FROM `ports_stp` WHERE `device_id` = ? AND `port_id` = ?' , array ( $device [ 'device_id' ], $stp_port [ 'port_id' ]))) {
2016-08-28 12:32:58 -05:00
dbInsert ( $stp_port , 'ports_stp' );
2016-01-14 16:54:07 +01:00
echo '+' ;
}
}
}
2016-08-28 12:32:58 -05:00
// Delete STP ports from db if absent in SNMP query
2016-01-14 16:54:07 +01:00
$stp_port_db = dbFetchRows ( 'SELECT * FROM `ports_stp` WHERE `device_id` = ?' , array ( $device [ 'device_id' ]));
//d_echo($stp_port_db);
2016-08-28 12:32:58 -05:00
foreach ( $stp_port_db as $port => $value ) {
2016-01-14 16:54:07 +01:00
$if_index = dbFetchCell ( 'SELECT `ifIndex` FROM `ports` WHERE `device_id` = ? AND `port_id` = ?' , array ( $device [ 'device_id' ], $value [ 'port_id' ]));
2016-08-28 12:32:58 -05:00
if ( $if_index != $stp_raw [ $if_index ][ 'dot1dStpPort' ]) {
2016-01-14 16:54:07 +01:00
dbDelete ( 'ports_stp' , '`device_id` = ? AND `port_id` = ?' , array ( $device [ 'device_id' ], $value [ 'port_id' ]));
echo '-' ;
}
}
2016-01-03 21:10:35 +01:00
}
2016-01-14 16:54:07 +01:00
unset ( $stp_raw , $stp , $stp_db , $stp_port , $stp_port_db );
2016-01-03 21:10:35 +01:00
echo " \n " ;