Issues (2963)

includes/discovery/route.inc.php (1 issue)

1
<?php
2
/* Copyright (C) 2014 Nicolas Armando <[email protected]>
3
 * Copyright (C) 2014 Mathieu Millet <[email protected]>
4
 * Copyright (C) 2019 PipoCanaja <[email protected]>
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
//We can use RFC1213 or IP-FORWARD-MIB or MPLS-L3VPN-STD-MIB
21
22
use App\Models\Device;
23
use LibreNMS\Config;
0 ignored issues
show
This use statement conflicts with another class in this namespace, Config. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
24
use LibreNMS\Util\IPv4;
25
26
$ipForwardMibRoutesNumber = snmp_get($device, 'IP-FORWARD-MIB::inetCidrRouteNumber.0', '-Osqn');
27
28
$ipForwardNb = snmp_get_multi($device, ['inetCidrRouteNumber.0', 'ipCidrRouteNumber.0'], '-OQUs', 'IP-FORWARD-MIB');
29
30
//Get the configured max routes number
31
$max_routes = 1000;
32
if (null != (Config::get('routes_max_number'))) {
33
    $max_routes = Config::get('routes_max_number');
34
}
35
36
//Init update/create tables;
37
$create_row = [];
38
$update_row = [];
39
$delete_row = [];
40
41
//store timestamp so all update / creation will be synced on same timestamp
42
$update_timestamp = dbFetchRows('select now() as now')[0]['now'];
43
44
//Load current DB entries:
45
$dbRoute = dbFetchRows('select * from `route` where `device_id` = ?', [$device['device_id']]);
46
foreach ($dbRoute as $dbRow) {
47
    $current = $mixed[$dbRow['context_name']][$dbRow['inetCidrRouteDestType']][$dbRow['inetCidrRouteDest']][$dbRow['inetCidrRoutePfxLen']][$dbRow['inetCidrRoutePolicy']][$dbRow['inetCidrRouteNextHopType']][$dbRow['inetCidrRouteNextHop']];
48
    if (isset($current) && isset($current['db']) && count($current['db']) > 0) {
49
        //We have duplicate routes in DB, we'll clean that.
50
        $delete_row[$dbRow['route_id']] = 1;
51
        $delete_row_data[$dbRow['route_id']] = $dbRow; //DEBUG DATA ONLY
52
    } else {
53
        $mixed[$dbRow['context_name']][$dbRow['inetCidrRouteDestType']][$dbRow['inetCidrRouteDest']][$dbRow['inetCidrRoutePfxLen']][$dbRow['inetCidrRoutePolicy']][$dbRow['inetCidrRouteNextHopType']][$dbRow['inetCidrRouteNextHop']]['db'] = $dbRow;
54
    }
55
}
56
57
//Not a single route will be discovered if the amount is over maximum
58
// To prevent any bad behaviour on routers holding the full internet table
59
60
//if the device does not support IP-FORWARD-MIB, we can still discover the ipv4 (only)
61
//routes using RFC1213 but no way to limit the amount of routes here !!
62
63
if (! isset($ipForwardNb['0']['inetCidrRouteNumber'])) {
64
    //RFC1213-MIB
65
    $mib = 'RFC1213-MIB';
66
    $tableRoute = [];
67
68
    $oid = '.1.3.6.1.2.1.4.21';
69
    $tableRoute = snmpwalk_group($device, $oid, $mib, 1, []);
70
    d_echo('Routing table:');
71
    d_echo($tableRoute);
72
    echo 'RFC1213 ';
73
    foreach ($tableRoute as $ipRoute) {
74
        if (empty($ipRoute['ipRouteDest']) || $ipRoute['ipRouteDest'] == '') {
75
            continue;
76
        }
77
78
        unset($entryClean);
79
        $entryClean['inetCidrRouteDestType'] = 'ipv4';
80
        $entryClean['inetCidrRouteDest'] = $ipRoute['ipRouteDest'];
81
        $inetCidrRoutePfxLen = IPv4::netmask2cidr($ipRoute['ipRouteMask']); //CONVERT
82
        $entryClean['inetCidrRoutePfxLen'] = $inetCidrRoutePfxLen;
83
        $entryClean['inetCidrRoutePolicy'] = $ipRoute['ipRouteInfo'];
84
        $entryClean['inetCidrRouteNextHopType'] = 'ipv4';
85
        $entryClean['inetCidrRouteNextHop'] = $ipRoute['ipRouteNextHop'];
86
        $entryClean['inetCidrRouteMetric1'] = $ipRoute['ipRouteMetric1'];
87
        $entryClean['inetCidrRouteNextHopAS'] = '0';
88
        $entryClean['inetCidrRouteProto'] = $ipRoute['ipRouteProto'];
89
        $entryClean['inetCidrRouteType'] = $ipRoute['ipRouteType'];
90
        $entryClean['inetCidrRouteIfIndex'] = $ipRoute['ipRouteIfIndex'];
91
        $entryClean['context_name'] = '';
92
        $entryClean['device_id'] = $device['device_id'];
93
        $entryClean['port_id'] = Device::find($device['device_id'])->ports()->where('ifIndex', '=', $entryClean['inetCidrRouteIfIndex'])->first()->port_id;
94
        $entryClean['updated_at'] = $update_timestamp;
95
        $current = $mixed['']['ipv4'][$inetCidrRouteDest][$inetCidrRoutePfxLen][$entryClean['inetCidrRoutePolicy']]['ipv4'][$inetCidrRouteNextHop];
96
        if (isset($current) && isset($current['db']) && count($current['db']) > 0 && $delete_row[$current['db']['route_id']] != 1) {
97
            //we already have a row in DB
98
            $entryClean['route_id'] = $current['db']['route_id'];
99
            $update_row[] = $entryClean;
100
        } else {
101
            $entry['created_at'] = ['NOW()'];
102
            $create_row[] = $entryClean;
103
        }
104
    }
105
}
106
107
// Not a single route will be discovered if the amount is over maximum
108
// To prevent any bad behaviour on routers holding the full internet table
109
110
// IP-FORWARD-MIB with inetCidrRouteTable
111
112
if (isset($ipForwardNb['0']['inetCidrRouteNumber']) && $ipForwardNb['0']['inetCidrRouteNumber'] < $max_routes) {
113
    // We have ip forward mib available
114
    d_echo('IP FORWARD MIB (with inetCidr support)');
115
    $mib = 'IP-FORWARD-MIB';
116
    $oid = '.1.3.6.1.2.1.4.24.7.1';
117
    $res = snmpwalk_group($device, $oid, $mib, 6, []);
118
    $ipForwardNb['0']['inetCidrRouteNumber'] = count($res); // Some cisco devices report ipv4+ipv6 but only include ipv6 in this table
119
    echo 'inetCidrRoute ';
120
    foreach ($res as $inetCidrRouteDestType => $next1) {
121
        //ipv4 or ipv6
122
        foreach ($next1 as $inetCidrRouteDest => $next2) {
123
            //we have only 1 child here, the mask
124
            $inetCidrRoutePfxLen = array_keys($next2)[0];
125
            $next3 = array_values($next2)[0];
126
            $inetCidrRoutePolicy = array_keys($next3)[0];
127
            $next4 = array_values($next3)[0];
128
            foreach ($next4 as $inetCidrRouteNextHopType => $next5) {
129
                foreach ($next5 as $inetCidrRouteNextHop => $entry) {
130
                    $entry['inetCidrRouteDestType'] = $inetCidrRouteDestType;
131
                    $entry['inetCidrRouteDest'] = normalize_snmp_ip_address($inetCidrRouteDest);
132
                    $entry['inetCidrRoutePfxLen'] = $inetCidrRoutePfxLen;
133
                    $entry['inetCidrRoutePolicy'] = $inetCidrRoutePolicy;
134
                    $entry['inetCidrRouteNextHopType'] = $inetCidrRouteNextHopType;
135
                    $entry['inetCidrRouteNextHop'] = normalize_snmp_ip_address($inetCidrRouteNextHop);
136
                    $entry['context_name'] = '';
137
                    $entry['device_id'] = $device['device_id'];
138
                    $entry['port_id'] = Device::find($device['device_id'])->ports()->where('ifIndex', '=', $entry['inetCidrRouteIfIndex'])->first()->port_id;
139
                    $entry['updated_at'] = $update_timestamp;
140
                    unset($entry['inetCidrRouteAge']);
141
                    unset($entry['inetCidrRouteMetric2']);
142
                    unset($entry['inetCidrRouteMetric3']);
143
                    unset($entry['inetCidrRouteMetric4']);
144
                    unset($entry['inetCidrRouteMetric5']);
145
                    unset($entry['inetCidrRouteStatus']);
146
                    $entryPerType[$inetCidrRouteDestType]++;
147
                    $current = $mixed[''][$inetCidrRouteDestType][$inetCidrRouteDest][$inetCidrRoutePfxLen][$inetCidrRoutePolicy][$inetCidrRouteNextHopType][$inetCidrRouteNextHop];
148
                    if (! empty($current['db']) && $delete_row[$current['db']['route_id']] != 1) {
149
                        //we already have a row in DB
150
                        $entry['route_id'] = $current['db']['route_id'];
151
                        $update_row[] = $entry;
152
                    } else {
153
                        d_echo(isset($current));
154
                        d_echo(isset($current['db']));
155
                        d_echo($current['db']);
156
                        d_echo($delete_row[$current['db']['route_id']]);
157
                        $entry['created_at'] = ['NOW()'];
158
                        $create_row[] = $entry;
159
                    }
160
                }
161
            }
162
        }
163
    }
164
165
    $ipForwardNb['0']['inetCidrRouteNumber'] = $entryPerType['ipv4'];
166
    // Some cisco devices report ipv4+ipv6 in inetCidrRouteNumber
167
    // But only include ipv6 in inetCidrRoute
168
    // So we count the real amount of ipv4 we get, in order to get the missing ipv4 from ipCidrRouteTable if needed
169
}
170
171
// IP-FORWARD-MIB with ipCidrRouteTable in case ipCidrRouteTable has more entries than inetCidrRouteTable (Some older routers)
172
173
if (isset($ipForwardNb['0']['ipCidrRouteNumber']) && $ipForwardNb['0']['ipCidrRouteNumber'] > $ipForwardNb['0']['inetCidrRouteNumber'] && $ipForwardNb['0']['ipCidrRouteNumber'] < $max_routes) {
174
    //device uses only ipCidrRoute and not inetCidrRoute
175
    d_echo('IP FORWARD MIB (without inetCidr support)');
176
    $mib = 'IP-FORWARD-MIB';
177
    $oid = '.1.3.6.1.2.1.4.24.4.1';
178
    $ipCidrTable = snmpwalk_group($device, $oid, $mib, 6, []);
179
    echo 'ipCidrRouteTable ';
180
    // we need to translate the values to inetCidr structure;
181
    //d_echo($ipCidrTable);
182
    foreach ($ipCidrTable as $inetCidrRouteDest => $next1) {
183
        foreach ($next1 as $ipCidrRouteMask => $next2) {
184
            foreach ($next2 as $ipCidrRouteTos => $next3) {
185
                foreach ($next3 as $inetCidrRouteNextHop => $entry) {
186
                    unset($entryClean);
187
                    $entryClean['inetCidrRouteDestType'] = 'ipv4';
188
                    $entryClean['inetCidrRouteDest'] = $inetCidrRouteDest;
189
                    $inetCidrRoutePfxLen = IPv4::netmask2cidr($entry['ipCidrRouteMask']); //CONVERT
190
                    $entryClean['inetCidrRoutePfxLen'] = $inetCidrRoutePfxLen;
191
                    $entryClean['inetCidrRoutePolicy'] = $entry['ipCidrRouteInfo'];
192
                    $entryClean['inetCidrRouteNextHopType'] = 'ipv4';
193
                    $entryClean['inetCidrRouteNextHop'] = $inetCidrRouteNextHop;
194
                    $entryClean['inetCidrRouteMetric1'] = $entry['ipCidrRouteMetric1'];
195
                    $entryClean['inetCidrRouteProto'] = $entry['ipCidrRouteProto'];
196
                    $entryClean['inetCidrRouteType'] = $entry['ipCidrRouteType'];
197
                    $entryClean['inetCidrRouteIfIndex'] = $entry['ipCidrRouteIfIndex'];
198
                    $entryClean['inetCidrRouteNextHopAS'] = $entry['ipCidrRouteNextHopAS'];
199
                    $entryClean['context_name'] = '';
200
                    $entryClean['device_id'] = $device['device_id'];
201
                    $entryClean['port_id'] = Device::find($device['device_id'])->ports()->where('ifIndex', '=', $entryClean['inetCidrRouteIfIndex'])->first()->port_id;
202
                    $entryClean['updated_at'] = $update_timestamp;
203
                    $current = $mixed['']['ipv4'][$inetCidrRouteDest][$inetCidrRoutePfxLen][$entryClean['inetCidrRoutePolicy']]['ipv4'][$inetCidrRouteNextHop];
204
                    if (isset($current) && isset($current['db']) && count($current['db']) > 0 && $delete_row[$current['db']['route_id']] != 1) {
205
                        //we already have a row in DB
206
                        $entryClean['route_id'] = $current['db']['route_id'];
207
                        $update_row[] = $entryClean;
208
                    } else {
209
                        $entryClean['created_at'] = ['NOW()'];
210
                        $create_row[] = $entryClean;
211
                    }
212
                }
213
            }
214
        }
215
    }
216
}
217
218
// We can now check if we have MPLS VPN routing table available :
219
// MPLS-L3VPN-STD-MIB::mplsL3VpnVrfRteTable
220
// Route numbers : MPLS-L3VPN-STD-MIB::mplsL3VpnVrfPerfCurrNumRoutes
221
222
$mib = 'MPLS-L3VPN-STD-MIB';
223
$oid = 'mplsL3VpnVrfPerfCurrNumRoutes';
224
$mpls_vpn_route_nb = snmpwalk_group($device, $oid, $mib, 6, []);
225
226
foreach ($mpls_vpn_route_nb as $vpnId => $route_nb) {
227
    if ($route_nb['mplsL3VpnVrfPerfCurrNumRoutes'] > $max_routes) {
228
        echo "Skipping all MPLS routes because vpn instance $vpnId has more than $max_routes routes.";
229
        $mpls_skip = 1;
230
    }
231
}
232
233
if ($mpls_skip != 1) {
234
    echo 'mplsL3VpnVrfRteTable ';
235
    // We can discover the routes;
236
    $oid = 'mplsL3VpnVrfRteTable';
237
    $mpls_route_table = snmpwalk_group($device, $oid, $mib, 7, []);
238
    foreach ($mpls_route_table as $vpnId => $inetCidrRouteTable) {
239
        foreach ($inetCidrRouteTable as $inetCidrRouteDestType => $next1) {
240
            //ipv4 or ipv6
241
            foreach ($next1 as $inetCidrRouteDest => $next2) {
242
                //we have only 1 child here, the mask
243
                $inetCidrRoutePfxLen = array_keys($next2)[0];
244
                $next3 = array_values($next2)[0];
245
                $inetCidrRoutePolicy = array_keys($next3)[0];
246
                $next4 = array_values($next3)[0];
247
                foreach ($next4 as $inetCidrRouteNextHopType => $next5) {
248
                    foreach ($next5 as $inetCidrRouteNextHop => $entry) {
249
                        $entry['inetCidrRouteDestType'] = $inetCidrRouteDestType;
250
                        $entry['inetCidrRouteDest'] = normalize_snmp_ip_address($inetCidrRouteDest);
251
                        $entry['inetCidrRoutePfxLen'] = $inetCidrRoutePfxLen;
252
                        $entry['inetCidrRoutePolicy'] = $inetCidrRoutePolicy;
253
                        $entry['inetCidrRouteNextHopType'] = $inetCidrRouteNextHopType;
254
                        $entry['inetCidrRouteNextHop'] = normalize_snmp_ip_address($inetCidrRouteNextHop);
255
                        $entry['context_name'] = $vpnId;
256
                        $entry['device_id'] = $device['device_id'];
257
                        $entry['inetCidrRouteIfIndex'] = $entry['mplsL3VpnVrfRteInetCidrIfIndex'];
258
                        $entry['port_id'] = Device::find($device['device_id'])->ports()->where('ifIndex', '=', $entry['inetCidrRouteIfIndex'])->first()->port_id;
259
                        $entry['updated_at'] = $update_timestamp;
260
                        $entry['inetCidrRouteType'] = $entry['mplsL3VpnVrfRteInetCidrType'];
261
                        $entry['inetCidrRouteProto'] = $entry['mplsL3VpnVrfRteInetCidrProto'];
262
                        $entry['inetCidrRouteMetric1'] = $entry['mplsL3VpnVrfRteInetCidrMetric1'];
263
                        $entry['inetCidrRouteNextHopAS'] = $entry['mplsL3VpnVrfRteInetCidrNextHopAS'];
264
                        unset($entry['mplsL3VpnVrfRteXCPointer']);
265
                        unset($entry['mplsL3VpnVrfRteInetCidrMetric1']);
266
                        unset($entry['mplsL3VpnVrfRteInetCidrMetric2']);
267
                        unset($entry['mplsL3VpnVrfRteInetCidrMetric3']);
268
                        unset($entry['mplsL3VpnVrfRteInetCidrMetric4']);
269
                        unset($entry['mplsL3VpnVrfRteInetCidrMetric5']);
270
                        unset($entry['mplsL3VpnVrfRteInetCidrAge']);
271
                        unset($entry['mplsL3VpnVrfRteInetCidrProto']);
272
                        unset($entry['mplsL3VpnVrfRteInetCidrType']);
273
                        unset($entry['mplsL3VpnVrfRteInetCidrStatus']);
274
                        unset($entry['mplsL3VpnVrfRteInetCidrIfIndex']);
275
                        unset($entry['mplsL3VpnVrfRteInetCidrNextHopAS']);
276
                        $current = $mixed[$vpnId][$inetCidrRouteDestType][$inetCidrRouteDest][$inetCidrRoutePfxLen][$inetCidrRoutePolicy][$inetCidrRouteNextHopType][$inetCidrRouteNextHop];
277
                        if (isset($current) && isset($current['db']) && count($current['db']) > 0 && $delete_row[$current['db']['route_id']] != 1) {
278
                            //we already have a row in DB
279
                            $entry['route_id'] = $current['db']['route_id'];
280
                            $update_row[] = $entry;
281
                        } else {
282
                            d_echo(isset($current));
283
                            d_echo(isset($current['db']));
284
                            d_echo($current['db']);
285
                            d_echo(count($current['db']));
286
                            d_echo($delete_row[$current['db']['route_id']]);
287
                            $entry['created_at'] = ['NOW()'];
288
                            $create_row[] = $entry;
289
                        }
290
                    }
291
                }
292
            }
293
        }
294
    }
295
}
296
echo "\nProcessing: ";
297
298
// We can now process the data into the DB
299
foreach ($delete_row as $k => $v) {
300
    if ($v > 0) {
301
        dbDelete(
302
            'route',
303
            '`route_id` = ?',
304
            [$k]
305
        );
306
        echo '-';
307
        d_echo($delete_row_data[$k]);
308
    }
309
}
310
311
foreach ($update_row as $upd_entry) {
312
    dbUpdate(
313
        $upd_entry,
314
        'route',
315
        '`route_id` = ?',
316
        [$upd_entry['route_id']]
317
    );
318
    echo '.';
319
}
320
321
foreach ($create_row as $new_entry) {
322
    $new_entry['created_at'] = $update_timestamp;
323
    dbInsert($new_entry, 'route');
324
    echo '+';
325
}
326
327
// EOF
328