Issues (2963)

includes/polling/stp.inc.php (2 issues)

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
It seems like $time_since_change can also be of type false; however, parameter $string of substr() 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 ignore-type  annotation

63
        $time_since_change = substr(/** @scrutinizer ignore-type */ $time_since_change, 0, -2); // convert to seconds since change
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 ignore-type  annotation

75
    $mac_array = explode(':', /** @scrutinizer ignore-type */ $mac_raw);
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