Merge pull request #2690 from vitalisator/stp

add basic STP/RSTP support
This commit is contained in:
Neil Lathwood
2016-01-10 22:45:24 +00:00
7 changed files with 337 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
<?php
$stp_raw = dbFetchRow('SELECT * FROM `stp` WHERE `device_id` = ?', array($device['device_id']));
$stp = array (
'Root bridge' => ($stp_raw['rootBridge'] == 1) ? 'Yes' : 'No',
'Bridge address (MAC)' => $stp_raw['bridgeAddress'],
'Protocol specification' => $stp_raw['protocolSpecification'],
'Priority (0-61440)' => $stp_raw['priority'],
'Time since topology change' => formatUptime($stp_raw['timeSinceTopologyChange']),
'Topology changes' => $stp_raw['topChanges'],
'Designated root (MAC)' => $stp_raw['designatedRoot'],
'Root cost' => $stp_raw['rootCost'],
'Root port' => $stp_raw['rootPort'],
'Max age (s)' => $stp_raw['maxAge'],
'Hello time (s)' => $stp_raw['helloTime'],
'Hold time (s)' => $stp_raw['holdTime'],
'Forward delay (s)' => $stp_raw['forwardDelay'],
'Bridge max age (s)' => $stp_raw['bridgeMaxAge'],
'Bridge hello time (s)' => $stp_raw['bridgeHelloTime'],
'Bridge forward delay (s)' => $stp_raw['bridgeForwardDelay']
);
foreach (array_keys($stp) as $key) {
echo "
<tr>
<td width=280 class=list-large>$key</td>
<td class=box-desc>$stp[$key]</td>
</tr>
";
}

View File

@@ -233,6 +233,14 @@ if (device_permitted($vars['device']) || $check_device == $vars['device']) {
</a>
</li>');
if (@dbFetchCell("SELECT 1 FROM stp WHERE device_id = '".$device['device_id']."'")) {
echo '<li class="'.$select['stp'].'">
<a href="'.generate_device_url($device, array('tab' => 'stp')).'">
<img src="images/16/chart_organisation.png" align="absmiddle" border="0" /> STP
</a>
</li>';
}
if (@dbFetchCell("SELECT COUNT(*) FROM `packages` WHERE device_id = '".$device['device_id']."'") > '0') {
echo '<li class="'.$select['packages'].'">
<a href="'.generate_device_url($device, array('tab' => 'packages')).'">

View File

@@ -0,0 +1,50 @@
<?php
$link_array = array(
'page' => 'device',
'device' => $device['device_id'],
'tab' => 'stp',
);
print_optionbar_start();
echo "<span style='font-weight: bold;'>STP</span> &#187; ";
if (!$vars['view']) {
$vars['view'] = 'basic';
}
$menu_options['basic'] = 'Basic';
// $menu_options['details'] = 'Details';
$sep = '';
foreach ($menu_options as $option => $text) {
echo $sep;
if ($vars['view'] == $option) {
echo "<span class='pagemenu-selected'>";
}
echo generate_link($text, $link_array, array('view' => $option));
if ($vars['view'] == $option) {
echo '</span>';
}
$sep = ' | ';
}
unset($sep);
print_optionbar_end();
echo '<table border="0" cellspacing="0" cellpadding="5" width="100%">';
$i = '1';
foreach (dbFetchRows("SELECT * FROM `stp` WHERE `device_id` = ? ORDER BY 'stp_id'", array($device['device_id'])) as $stp) {
include 'includes/print-stp.inc.php';
$i++;
}
echo '</table>';
$pagetitle[] = 'STP';

View File

@@ -708,6 +708,7 @@ $config['poller_modules']['applications'] = 1;
$config['poller_modules']['cisco-asa-firewall'] = 1;
$config['poller_modules']['mib'] = 0;
$config['poller_modules']['cisco-voice'] = 1;
$config['poller_modules']['stp'] = 1;
// List of discovery modules. Need to be in this array to be
// considered for execution.
@@ -739,6 +740,7 @@ $config['discovery_modules']['toner'] = 1;
$config['discovery_modules']['ucd-diskio'] = 1;
$config['discovery_modules']['services'] = 1;
$config['discovery_modules']['charge'] = 1;
$config['discovery_modules']['stp'] = 1;
$config['modules_compat']['rfc1628']['liebert'] = 1;
$config['modules_compat']['rfc1628']['netmanplus'] = 1;

View File

@@ -0,0 +1,118 @@
<?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.1D-2004, (STP, RSTP)
* needs RSTP-MIB
*/
echo "Spanning Tree: ";
// 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
}
else {
$time_since_change = '0';
}
$stp['timeSinceTopologyChange'] = $time_since_change;
// designated root is stored in format 2 octet bridge priority + MAC address, so we need to normalize it
$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);
foreach($mac_array as &$octet) {
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';
}
else {
$stp['rootBridge'] = '0';
}
d_echo($stp);
if ($stp_raw[0]['version'] == '3') {
echo "RSTP ";
}
else {
echo "STP ";
}
if (!$stp_db['bridgeAddress'] && $stp['bridgeAddress']) {
dbInsert($stp,'stp');
log_event('STP added, bridge address: '.$stp['bridgeAddress'], $device, 'stp');
echo '+';
}
if ($stp_db['bridgeAddress'] && !$stp['bridgeAddress']) {
dbDelete('stp','device_id = ?', array($device['device_id']));
log_event('STP removed', $device, 'stp');
echo '-';
}
}
unset($stp_raw, $stp, $stp_db);
echo "\n";

View File

@@ -0,0 +1,127 @@
<?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.1D-2004, (STP, RSTP)
* needs RSTP-MIB
*/
echo "Spanning Tree: ";
// 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
}
else {
$time_since_change = '0';
}
$stp['timeSinceTopologyChange'] = $time_since_change;
// designated root is stored in format 2 octet bridge priority + MAC address, so we need to normalize it
$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);
foreach($mac_array as &$octet) {
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';
}
else {
$stp['rootBridge'] = '0';
}
d_echo($stp);
if ($stp_db['bridgeAddress'] && $stp['bridgeAddress']) {
// Logging if designated root changed since last db update
if ($stp_db['designatedRoot'] != $stp['designatedRoot']) {
log_event('STP designated root changed: '.$stp_db['designatedRoot'].' > '.$stp['designatedRoot'], $device, 'stp');
}
// Logging if designated root port changed since last db update
if ($stp_db['rootPort'] != $stp['rootPort']) {
log_event('STP root port changed: '.$stp_db['rootPort'].' > '.$stp['rootPort'], $device, 'stp');
}
// Logging if topology changed since last db update
if ($stp_db['timeSinceTopologyChange'] > $stp['timeSinceTopologyChange']) {
// FIXME log_event should log really changing time, not polling time
// but upstream function do not care about this at the moment.
//
// saw same problem with this line librenms/includes/polling/system.inc.php
// log_event('Device rebooted after '.formatUptime($device['uptime']), $device, 'reboot', $device['uptime']);
// ToDo fix log_event()
//
//log_event('STP topology changed after: '.formatUptime($stp['timeSinceTopologyChange']), $device, 'stp', $stp['timeSinceTopologyChange']);
log_event('STP topology changed after: '.formatUptime($stp['timeSinceTopologyChange']), $device, 'stp');
}
// Write to db
dbUpdate($stp,'stp','device_id = ?', array($device['device_id']));
echo '.';
}
}
unset($stp_raw, $stp, $stp_db);
echo "\n";

3
sql-schema/086.sql Normal file
View File

@@ -0,0 +1,3 @@
CREATE TABLE IF NOT EXISTS `stp` (`stp_id` int(11) NOT NULL, `device_id` int(11) NOT NULL, `rootBridge` tinyint(1) NOT NULL, `bridgeAddress` varchar(32) NOT NULL, `protocolSpecification` varchar(16) NOT NULL, `priority` mediumint(9) NOT NULL, `timeSinceTopologyChange` varchar(32) NOT NULL, `topChanges` mediumint(9) NOT NULL, `designatedRoot` varchar(32) NOT NULL, `rootCost` mediumint(9) NOT NULL, `rootPort` mediumint(9) NOT NULL, `maxAge` mediumint(9) NOT NULL, `helloTime` mediumint(9) NOT NULL, `holdTime` mediumint(9) NOT NULL, `forwardDelay` mediumint(9) NOT NULL, `bridgeMaxAge` smallint(6) NOT NULL, `bridgeHelloTime` smallint(6) NOT NULL, `bridgeForwardDelay` smallint(6) NOT NULL) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
ALTER TABLE `stp` ADD PRIMARY KEY (`stp_id`), ADD KEY `stp_host` (`device_id`);
ALTER TABLE `stp` MODIFY `stp_id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=1;