1 | <?php |
||||
2 | /* |
||||
3 | * LibreNMS |
||||
4 | * |
||||
5 | * Copyright (c) 2015 Vitali Kari <[email protected]> |
||||
6 | * |
||||
7 | * This program is free software: you can redistribute it and/or modify it |
||||
8 | * under the terms of the GNU General Public License as published by the |
||||
9 | * Free Software Foundation, either version 3 of the License, or (at your |
||||
10 | * option) any later version. Please see LICENSE.txt at the top level of |
||||
11 | * the source code distribution for details. |
||||
12 | * |
||||
13 | * Based on IEEE-802.1D-2004, (STP, RSTP) |
||||
14 | * needs RSTP-MIB |
||||
15 | */ |
||||
16 | |||||
17 | // Pre-cache existing state of STP for this device from database |
||||
18 | $stp_db = dbFetchRow('SELECT * FROM `stp` WHERE `device_id` = ?', [$device['device_id']]); |
||||
19 | |||||
20 | $stpprotocol = snmp_get($device, 'dot1dStpProtocolSpecification.0', '-Oqv', 'RSTP-MIB'); |
||||
21 | |||||
22 | // FIXME I don't know what "unknown" means, perhaps MSTP? (saw it on some cisco devices) |
||||
23 | // But we can try to retrieve data |
||||
24 | if ($stpprotocol == 'ieee8021d' || $stpprotocol == 'unknown') { |
||||
25 | // set time multiplier to convert from centiseconds to seconds |
||||
26 | // all time values are stored in databese as seconds |
||||
27 | $tm = '0.01'; |
||||
28 | // some vendors like PBN dont follow the 802.1D implementation and use seconds in SNMP |
||||
29 | if ($device['os'] == 'pbn') { |
||||
30 | preg_match('/^.* Build (?<build>\d+)/', $device['version'], $version); |
||||
31 | if ($version['build'] <= 16607) { // Buggy version :-( |
||||
32 | $tm = '1'; |
||||
33 | } |
||||
34 | } |
||||
35 | |||||
36 | // read the 802.1D subtree |
||||
37 | $stp_raw = snmpwalk_cache_oid($device, 'dot1dStp', [], 'RSTP-MIB'); |
||||
38 | d_echo($stp_raw); |
||||
39 | $stp = [ |
||||
40 | 'protocolSpecification' => $stp_raw[0]['dot1dStpProtocolSpecification'], |
||||
41 | 'priority' => $stp_raw[0]['dot1dStpPriority'], |
||||
42 | 'topChanges' => $stp_raw[0]['dot1dStpTopChanges'], |
||||
43 | 'rootCost' => $stp_raw[0]['dot1dStpRootCost'], |
||||
44 | 'rootPort' => $stp_raw[0]['dot1dStpRootPort'], |
||||
45 | 'maxAge' => $stp_raw[0]['dot1dStpMaxAge'] * $tm, |
||||
46 | 'helloTime' => $stp_raw[0]['dot1dStpHelloTime'] * $tm, |
||||
47 | 'holdTime' => $stp_raw[0]['dot1dStpHoldTime'] * $tm, |
||||
48 | 'forwardDelay' => $stp_raw[0]['dot1dStpForwardDelay'] * $tm, |
||||
49 | 'bridgeMaxAge' => $stp_raw[0]['dot1dStpBridgeMaxAge'] * $tm, |
||||
50 | 'bridgeHelloTime' => $stp_raw[0]['dot1dStpBridgeHelloTime'] * $tm, |
||||
51 | 'bridgeForwardDelay' => $stp_raw[0]['dot1dStpBridgeForwardDelay'] * $tm, |
||||
52 | ]; |
||||
53 | |||||
54 | // set device binding |
||||
55 | $stp['device_id'] = $device['device_id']; |
||||
56 | |||||
57 | // read the 802.1D bridge address and set as MAC in database |
||||
58 | $mac_raw = snmp_get($device, 'dot1dBaseBridgeAddress.0', '-Oqv', 'RSTP-MIB'); |
||||
59 | |||||
60 | // read Time as timetics (in hundredths of a seconds) since last topology change and convert to seconds |
||||
61 | $time_since_change = snmp_get($device, 'dot1dStpTimeSinceTopologyChange.0', '-Ovt', 'RSTP-MIB'); |
||||
62 | if ($time_since_change > '100') { |
||||
63 | $time_since_change = substr($time_since_change, 0, -2); // convert to seconds since change |
||||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
64 | } else { |
||||
65 | $time_since_change = '0'; |
||||
66 | } |
||||
67 | $stp['timeSinceTopologyChange'] = $time_since_change; |
||||
68 | |||||
69 | // designated root is stored in format 2 octet bridge priority + MAC address, so we need to normalize it |
||||
70 | $dr = str_replace([' ', ':', '-'], '', strtolower($stp_raw[0]['dot1dStpDesignatedRoot'])); |
||||
71 | $dr = substr($dr, -12); //remove first two octets |
||||
72 | $stp['designatedRoot'] = $dr; |
||||
73 | |||||
74 | // normalize the MAC |
||||
75 | $mac_array = explode(':', $mac_raw); |
||||
0 ignored issues
–
show
It seems like
$mac_raw can also be of type false ; however, parameter $string of explode() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
76 | foreach ($mac_array as &$octet) { |
||||
77 | if (strlen($octet) < 2) { |
||||
78 | $octet = '0' . $octet; // add suppressed 0 |
||||
79 | } |
||||
80 | } |
||||
81 | $stp['bridgeAddress'] = implode($mac_array); |
||||
82 | |||||
83 | // I'm the boss? |
||||
84 | if ($stp['bridgeAddress'] == $stp['designatedRoot']) { |
||||
85 | $stp['rootBridge'] = '1'; |
||||
86 | } else { |
||||
87 | $stp['rootBridge'] = '0'; |
||||
88 | } |
||||
89 | |||||
90 | d_echo($stp); |
||||
91 | |||||
92 | if ($stp_db['bridgeAddress'] && $stp['bridgeAddress']) { |
||||
93 | // Logging if designated root changed since last db update |
||||
94 | if ($stp_db['designatedRoot'] != $stp['designatedRoot']) { |
||||
95 | log_event('STP designated root changed: ' . $stp_db['designatedRoot'] . ' > ' . $stp['designatedRoot'], $device, 'stp', 4); |
||||
96 | } |
||||
97 | |||||
98 | // Logging if designated root port changed since last db update |
||||
99 | if (isset($stp['rootPort']) && $stp_db['rootPort'] != $stp['rootPort']) { |
||||
100 | log_event('STP root port changed: ' . $stp_db['rootPort'] . ' > ' . $stp['rootPort'], $device, 'stp', 4); |
||||
101 | } |
||||
102 | |||||
103 | // Logging if topology changed since last db update |
||||
104 | if ($stp_db['timeSinceTopologyChange'] > $stp['timeSinceTopologyChange']) { |
||||
105 | // FIXME log_event should log really changing time, not polling time |
||||
106 | // but upstream function do not care about this at the moment. |
||||
107 | // |
||||
108 | // saw same problem with this line librenms/includes/polling/system.inc.php |
||||
109 | // log_event('Device rebooted after '.formatUptime($device['uptime']), $device, 'reboot', $device['uptime']); |
||||
110 | // ToDo fix log_event() |
||||
111 | // |
||||
112 | //log_event('STP topology changed after: '.formatUptime($stp['timeSinceTopologyChange']), $device, 'stp', $stp['timeSinceTopologyChange']); |
||||
113 | log_event('STP topology changed after: ' . \LibreNMS\Util\Time::formatInterval($stp['timeSinceTopologyChange']), $device, 'stp', 4); |
||||
114 | } |
||||
115 | // Write to db |
||||
116 | dbUpdate($stp, 'stp', 'device_id = ?', [$device['device_id']]); |
||||
117 | echo '.'; |
||||
118 | } |
||||
119 | |||||
120 | // STP port related stuff |
||||
121 | foreach ($stp_raw as $port => $value) { |
||||
122 | if ($port) { // $stp_raw[0] ist not port related so we skip this one |
||||
123 | $stp_port = [ |
||||
124 | 'priority' => $stp_raw[$port]['dot1dStpPortPriority'], |
||||
125 | 'state' => $stp_raw[$port]['dot1dStpPortState'], |
||||
126 | 'enable' => $stp_raw[$port]['dot1dStpPortEnable'], |
||||
127 | 'pathCost' => $stp_raw[$port]['dot1dStpPortPathCost'], |
||||
128 | 'designatedCost' => $stp_raw[$port]['dot1dStpPortDesignatedCost'], |
||||
129 | 'designatedPort' => $stp_raw[$port]['dot1dStpPortDesignatedPort'], |
||||
130 | 'forwardTransitions' => $stp_raw[$port]['dot1dStpPortForwardTransitions'], |
||||
131 | ]; |
||||
132 | |||||
133 | // set device binding |
||||
134 | $stp_port['device_id'] = $device['device_id']; |
||||
135 | |||||
136 | // set port binding |
||||
137 | $stp_port['port_id'] = dbFetchCell('SELECT port_id FROM `ports` WHERE `device_id` = ? AND `ifIndex` = ?', [$device['device_id'], $stp_raw[$port]['dot1dStpPort']]); |
||||
138 | |||||
139 | $dr = str_replace(['.', ' ', ':', '-'], '', strtolower($stp_raw[$port]['dot1dStpPortDesignatedRoot'])); |
||||
140 | $dr = substr($dr, -12); //remove first two octets |
||||
141 | $stp_port['designatedRoot'] = $dr; |
||||
142 | |||||
143 | $db = str_replace(['.', ' ', ':', '-'], '', strtolower($stp_raw[$port]['dot1dStpPortDesignatedBridge'])); |
||||
144 | $db = substr($db, -12); //remove first two octets |
||||
145 | $stp_port['designatedBridge'] = $db; |
||||
146 | |||||
147 | if ($device['os'] == 'pbn') { |
||||
148 | // It seems that PBN guys don't care about ieee 802.1d :-( |
||||
149 | // So try to find the right port with some crazy conversations |
||||
150 | $dp_value = dechex($stp_port['priority']); |
||||
151 | $dp_value = $dp_value . '00'; |
||||
152 | $dp_value = hexdec($dp_value); |
||||
153 | if ($stp_raw[$port]['dot1dStpPortDesignatedPort']) { |
||||
154 | $dp = $stp_raw[$port]['dot1dStpPortDesignatedPort'] - $dp_value; |
||||
155 | $stp_port['designatedPort'] = $dp; |
||||
156 | } |
||||
157 | } else { |
||||
158 | if (preg_match('/-/', $stp_raw[$port]['dot1dStpPortDesignatedPort'])) { |
||||
159 | // Syntax with "priority" dash "portID" like so : 32768-54, both in decimal |
||||
160 | $dp = explode('-', $stp_raw[$port]['dot1dStpPortDesignatedPort'])[1]; |
||||
161 | $stp_port['designatedPort'] = $dp; |
||||
162 | } else { |
||||
163 | // Port saved in format priority+port (ieee 802.1d-1998: clause 8.5.5.1) |
||||
164 | $dp = substr($stp_raw[$port]['dot1dStpPortDesignatedPort'], -2); //discard the first octet (priority part) |
||||
165 | $stp_port['designatedPort'] = hexdec($dp); |
||||
166 | } |
||||
167 | } |
||||
168 | |||||
169 | // Update db |
||||
170 | dbUpdate($stp_port, 'ports_stp', '`device_id` = ? AND `port_id` = ?', [$device['device_id'], $stp_port['port_id']]); |
||||
171 | echo '.'; |
||||
172 | } |
||||
173 | } |
||||
174 | } |
||||
175 | |||||
176 | unset( |
||||
177 | $stp_raw, |
||||
178 | $stp, |
||||
179 | $stp_db, |
||||
180 | $stp_port, |
||||
181 | $mac_array, |
||||
182 | $stpprotocol, |
||||
183 | $tm, |
||||
184 | $mac_raw, |
||||
185 | $time_since_change, |
||||
186 | $dr, |
||||
187 | $octet, |
||||
188 | $port, |
||||
189 | $db, |
||||
190 | $dp |
||||
191 | ); |
||||
192 | echo "\n"; |
||||
193 |