From bf181b9dc2ef0b2df5a4e874fbf7dfe3810e346e Mon Sep 17 00:00:00 2001
From: PipoCanaja <38363551+PipoCanaja@users.noreply.github.com>
Date: Sun, 17 Nov 2019 16:30:43 +0100
Subject: [PATCH] Added support for routing table collection in discovery
(#10182)
* Clean broken VRF lite code
* Change DB table for route discovery
* Add VRF simple support
* add port_id to db and discovery
* static-fy the translation arrays
* sort and search cleaning
* Sorting refactor and validation
* formatItem shortened
* Handle ifIndex==0 meaning no next hop defined (MPLS)
* Sync all create/updates
* purge in daily
* remove old route table
* get rid of inetCidrRouteNextHop_device_id
* fix wonky column orders
* add route snmprec
* fix sorting by interface
* Move to new config
* rename to route the new table
* Properly display ipv6 compressed addresses
* Translation before merge ./lnms translation:generate
* Update manifest
---
LibreNMS/Util/IPv6.php | 21 +-
LibreNMS/Util/Rewrite.php | 5 +
.../Table/RoutesTablesController.php | 195 ++++++++
app/Models/Route.php | 51 +++
daily.php | 5 +-
daily.sh | 1 +
.../2019_04_22_220000_update_route_table.php | 64 +++
doc/Support/Discovery Support.md | 4 +-
html/mix-manifest.json | 12 +-
includes/discovery/route.inc.php | 418 ++++++++++++------
includes/functions.php | 8 +
includes/html/pages/device.inc.php | 5 +
includes/html/pages/device/routing.inc.php | 1 +
.../html/pages/device/routing/routes.inc.php | 54 +++
misc/config_definitions.json | 14 +
misc/db_schema.yaml | 27 +-
resources/lang/en/settings.php | 11 +-
routes/web.php | 1 +
tests/module_tables.yaml | 4 +
tests/snmpsim/vrp_route.snmprec | 152 +++++++
20 files changed, 902 insertions(+), 151 deletions(-)
create mode 100644 app/Http/Controllers/Table/RoutesTablesController.php
create mode 100644 app/Models/Route.php
create mode 100644 database/migrations/2019_04_22_220000_update_route_table.php
create mode 100644 includes/html/pages/device/routing/routes.inc.php
create mode 100644 tests/snmpsim/vrp_route.snmprec
diff --git a/LibreNMS/Util/IPv6.php b/LibreNMS/Util/IPv6.php
index 445af00e88..78675810e2 100644
--- a/LibreNMS/Util/IPv6.php
+++ b/LibreNMS/Util/IPv6.php
@@ -46,6 +46,21 @@ class IPv6 extends IP
$this->ip = $this->compressed(); // store in compressed format
}
+ /**
+ * Convert a MySQL binary v6 (16-byte) IP address to a printable string.
+ * @param string $ip A binary string containing an IP address, as returned from MySQL's INET6_ATON function
+ * @return string Empty if not valid.
+ */
+ // Fuction is from http://uk3.php.net/manual/en/function.inet-ntop.php
+ public static function ntop($ip)
+ {
+ $len = strlen($ip);
+ if ($len == 16) {
+ return inet_ntop(pack('A' . $len, $ip));
+ }
+ return '';
+ }
+
/**
* Check if the supplied IP is valid.
* @param string $ipv6
@@ -68,7 +83,7 @@ class IPv6 extends IP
*/
public function compressed()
{
- return inet6_ntop(inet_pton($this->ip));
+ return self::ntop(inet_pton($this->ip));
}
/**
@@ -94,7 +109,7 @@ class IPv6 extends IP
}
}
array_unshift($net_bytes, 'n*'); // add pack format
- return inet6_ntop(call_user_func_array('pack', $net_bytes));
+ return self::ntop(call_user_func_array('pack', $net_bytes));
}
/**
@@ -144,7 +159,7 @@ class IPv6 extends IP
// zero pad
$parts = explode(':', $ip, 8);
return implode(':', array_map(function ($section) {
- return zeropad($section, 4);
+ return Rewrite::zeropad($section, 4);
}, $parts));
}
diff --git a/LibreNMS/Util/Rewrite.php b/LibreNMS/Util/Rewrite.php
index 842004f569..08197fd445 100644
--- a/LibreNMS/Util/Rewrite.php
+++ b/LibreNMS/Util/Rewrite.php
@@ -364,4 +364,9 @@ class Rewrite
return $guests[$guest_id] ?? $guest_id;
}
+
+ public static function zeropad($num, $length = 2)
+ {
+ return str_pad($num, $length, '0', STR_PAD_LEFT);
+ }
}
diff --git a/app/Http/Controllers/Table/RoutesTablesController.php b/app/Http/Controllers/Table/RoutesTablesController.php
new file mode 100644
index 0000000000..eb5d9d1667
--- /dev/null
+++ b/app/Http/Controllers/Table/RoutesTablesController.php
@@ -0,0 +1,195 @@
+.
+ *
+ * @package LibreNMS
+ * @link http://librenms.org
+ * @copyright 2019 PipoCanaja
+ * @author PipoCanaja
+ */
+
+namespace App\Http\Controllers\Table;
+
+use App\Models\Ipv4Address;
+use App\Models\Ipv4Network;
+use App\Models\Ipv6Address;
+use App\Models\Route;
+use App\Models\Port;
+use App\Models\Device;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+use LibreNMS\Util\IP;
+use LibreNMS\Util\Rewrite;
+use LibreNMS\Util\Url;
+use LibreNMS\Exceptions\InvalidIpException;
+
+class RoutesTablesController extends TableController
+{
+ protected $ipCache = [];
+
+ protected function rules()
+ {
+ return [
+ 'device_id' => 'nullable|integer',
+ 'searchby' => 'in:inetCidrRouteNextHop,inetCidrRouteDest',
+ ];
+ }
+
+ protected function filterFields($request)
+ {
+ return [
+ 'route.context_name' => 'context_name',
+ 'route.inetCidrRouteProto' => 'proto',
+ ];
+ }
+
+ /**
+ * Defines the base query for this resource
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder
+ */
+ protected function baseQuery($request)
+ {
+ $join = function ($query) {
+ $query->on('ports.port_id', 'route.port_id');
+ };
+ $showAllRoutes = trim(\Request::get('showAllRoutes'));
+ if ($request->device_id && $showAllRoutes == 'false') {
+ $query=Route::hasAccess($request->user())
+ ->leftJoin('ports', $join)
+ ->where('route.device_id', $request->device_id)
+ ->where('updated_at', Route::hasAccess($request->user())
+ ->where('route.device_id', $request->device_id)
+ ->select('updated_at')
+ ->max('updated_at'));
+ return $query;
+ }
+ if ($request->device_id && $showAllRoutes == 'true') {
+ $query=Route::hasAccess($request->user())
+ ->leftJoin('ports', $join)
+ ->where('route.device_id', $request->device_id);
+ return $query;
+ }
+ return Route::hasAccess($request->user())
+ ->leftJoin('ports', $join);
+ }
+
+ /**
+ * @param string $search
+ * @param Builder $query
+ * @param array $fields
+ * @return Builder|\Illuminate\Database\Query\Builder
+ */
+ protected function search($search, $query, $fields = [])
+ {
+ if ($search = trim(\Request::get('searchPhrase'))) {
+ $searchLike = '%' . $search . '%';
+ return $query->where('route.inetCidrRouteNextHop', 'like', $searchLike)
+ ->orWhere('route.inetCidrRouteDest', 'like', $searchLike);
+ }
+ return $query;
+ }
+
+ /**
+ * @param Request $request
+ * @param Builder $query
+ * @return Builder
+ */
+ public function sort($request, $query)
+ {
+ $sort = $request->get('sort');
+ if (isset($sort['inetCidrRouteIfIndex'])) {
+ $query->orderBy('ifDescr', $sort['inetCidrRouteIfIndex'])
+ ->orderBy('inetCidrRouteIfIndex', $sort['inetCidrRouteIfIndex']);
+ }
+ // Simple fields to sort
+ $s_fields = [
+ 'inetCidrRouteDestType',
+ 'inetCidrRouteType',
+ 'inetCidrRouteMetric1',
+ 'inetCidrRoutePfxLen',
+ 'inetCidrRouteNextHop',
+ 'updated_at',
+ 'created_at',
+ 'context_name',
+ 'inetCidrRouteDest'
+ ];
+ foreach ($s_fields as $s_field) {
+ if (isset($sort[$s_field])) {
+ $query->orderBy($s_field, $sort[$s_field]);
+ }
+ }
+ return $query;
+ }
+
+ public function formatItem($route_entry)
+ {
+ $item = $route_entry->toArray();
+
+ if ($route_entry->updated_at) {
+ $item['updated_at'] = $route_entry->updated_at->diffForHumans();
+ }
+ if ($route_entry->created_at) {
+ $item['created_at'] = $route_entry->created_at->toDateTimeString();
+ }
+ if ($item['inetCidrRouteIfIndex'] == 0) {
+ $item['inetCidrRouteIfIndex'] = 'Undefined';
+ }
+ if ($route_entry->inetCidrRouteNextHop) {
+ try {
+ $obj_inetCidrRouteNextHop = IP::parse($route_entry->inetCidrRouteNextHop);
+ $item['inetCidrRouteNextHop'] = $obj_inetCidrRouteNextHop->compressed();
+ } catch (Exception $e) {
+ $item['inetCidrRouteNextHop'] = $route_entry->inetCidrRouteNextHop;
+ }
+ }
+ if ($route_entry->inetCidrRouteDest) {
+ try {
+ $obj_inetCidrRouteDest = IP::parse($route_entry->inetCidrRouteDest);
+ $item['inetCidrRouteDest'] = $obj_inetCidrRouteDest->compressed();
+ } catch (Exception $e) {
+ $item['inetCidrRouteDest'] = $route_entry->inetCidrRouteDest;
+ }
+ }
+ $item['inetCidrRouteIfIndex'] = 'ifIndex ' . $item['inetCidrRouteIfIndex'];
+ if ($port = $route_entry->port()->first()) {
+ $item['inetCidrRouteIfIndex'] = Url::portLink($port, $port->getShortLabel());
+ }
+ $device = Device::findByIp($route_entry->inetCidrRouteNextHop);
+ if ($device) {
+ if ($device->device_id == $route_entry->device_id || in_array($route_entry->inetCidrRouteNextHop, ['127.0.0.1', '::1'])) {
+ $item['inetCidrRouteNextHop'] = Url::deviceLink($device, "localhost");
+ } else {
+ $item['inetCidrRouteNextHop'] = $item['inetCidrRouteNextHop'] . "
(" . Url::deviceLink($device) . ")";
+ }
+ }
+ if ($route_entry->inetCidrRouteProto && $route_entry::$translateProto[$route_entry->inetCidrRouteProto]) {
+ $item['inetCidrRouteProto'] = $route_entry::$translateProto[$route_entry->inetCidrRouteProto];
+ }
+ if ($route_entry->inetCidrRouteType && $route_entry::$translateType[$route_entry->inetCidrRouteType]) {
+ $item['inetCidrRouteType'] = $route_entry::$translateType[$route_entry->inetCidrRouteType];
+ }
+ $item['context_name'] = '[global]';
+ if ($route_entry->context_name != '') {
+ $item['context_name'] = '' . $route_entry->context_name . '' ;
+ }
+ return $item;
+ }
+}
diff --git a/app/Models/Route.php b/app/Models/Route.php
new file mode 100644
index 0000000000..13242b7fdf
--- /dev/null
+++ b/app/Models/Route.php
@@ -0,0 +1,51 @@
+belongsTo('App\Models\Device', 'device_id', 'device_id');
+ }
+
+ public function port()
+ {
+ return $this->belongsTo('App\Models\Port', 'port_id', 'port_id');
+ }
+}
diff --git a/daily.php b/daily.php
index 7de5822dd6..d3061363e7 100644
--- a/daily.php
+++ b/daily.php
@@ -96,7 +96,10 @@ if ($options['f'] === 'ports_fdb') {
$ret = lock_and_purge('ports_fdb', 'updated_at < DATE_SUB(NOW(), INTERVAL ? DAY)');
exit($ret);
}
-
+if ($options['f'] === 'route') {
+ $ret = lock_and_purge('route', 'updated_at < DATE_SUB(NOW(), INTERVAL ? DAY)');
+ exit($ret);
+}
if ($options['f'] === 'eventlog') {
$ret = lock_and_purge('eventlog', 'datetime < DATE_SUB(NOW(), INTERVAL ? DAY)');
exit($ret);
diff --git a/daily.sh b/daily.sh
index a2d1aa4920..e40744295b 100755
--- a/daily.sh
+++ b/daily.sh
@@ -275,6 +275,7 @@ main () {
"alert_log"
"rrd_purge"
"ports_fdb"
+ "route"
"ports_purge");
call_daily_php "${options[@]}";
;;
diff --git a/database/migrations/2019_04_22_220000_update_route_table.php b/database/migrations/2019_04_22_220000_update_route_table.php
new file mode 100644
index 0000000000..d388081e5d
--- /dev/null
+++ b/database/migrations/2019_04_22_220000_update_route_table.php
@@ -0,0 +1,64 @@
+increments('route_id');
+ $table->timestamps();
+ $table->unsignedInteger('device_id');
+ $table->unsignedInteger('port_id');
+ $table->string('context_name')->nullable();
+ $table->bigInteger('inetCidrRouteIfIndex');
+ $table->unsignedInteger('inetCidrRouteType');
+ $table->unsignedInteger('inetCidrRouteProto');
+ $table->unsignedInteger('inetCidrRouteNextHopAS');
+ $table->unsignedInteger('inetCidrRouteMetric1');
+ $table->string('inetCidrRouteDestType');
+ $table->string('inetCidrRouteDest');
+ $table->string('inetCidrRouteNextHopType');
+ $table->string('inetCidrRouteNextHop');
+ $table->string('inetCidrRoutePolicy');
+ $table->unsignedInteger('inetCidrRoutePfxLen');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::drop('route');
+ // Create the old route table to reverse this.
+ Schema::create('route', function (Blueprint $table) {
+ $table->unsignedInteger('device_id');
+ $table->string('context_name', 128);
+ $table->string('ipRouteDest', 39);
+ $table->string('ipRouteIfIndex', 256)->nullable();
+ $table->string('ipRouteMetric', 256);
+ $table->string('ipRouteNextHop', 39);
+ $table->string('ipRouteType', 256);
+ $table->string('ipRouteProto', 256);
+ $table->unsignedInteger('discoveredAt');
+ $table->string('ipRouteMask', 256);
+ $table->index(['device_id','context_name','ipRouteDest','ipRouteNextHop'], 'device');
+ });
+ }
+}
diff --git a/doc/Support/Discovery Support.md b/doc/Support/Discovery Support.md
index 55d362a38e..cd7ece2a46 100644
--- a/doc/Support/Discovery Support.md
+++ b/doc/Support/Discovery Support.md
@@ -145,7 +145,9 @@ configured to be ignored by config options.
`ipv6-addresses`: IPv6 Address detection
-`route`: Route detection
+`route`: This module will load the routing table of the device. The default route
+ limit is 1000 (configurable in config.php with
+ ```$config['routes']['max_number'] = 1000;```), with history data.
`sensors`: Sensor detection such as Temperature, Humidity, Voltages + More
diff --git a/html/mix-manifest.json b/html/mix-manifest.json
index a1187fa80f..0e1976a2fb 100644
--- a/html/mix-manifest.json
+++ b/html/mix-manifest.json
@@ -3,10 +3,10 @@
"/css/app.css": "/css/app.css?id=17e56994706c74ee9663",
"/js/manifest.js": "/js/manifest.js?id=3c768977c2574a34506e",
"/js/vendor.js": "/js/vendor.js?id=00c1d21ecfea78860e09",
- "/js/lang/de.js": "/js/lang/de.js?id=18b0b0e06813d1afed92",
- "/js/lang/en.js": "/js/lang/en.js?id=a31a978859a3e4fe73c7",
- "/js/lang/fr.js": "/js/lang/fr.js?id=07da32f987ba907e1f7f",
- "/js/lang/ru.js": "/js/lang/ru.js?id=e10e85f321f1395378b6",
- "/js/lang/uk.js": "/js/lang/uk.js?id=c8d4937e3ca47b60b7ac",
- "/js/lang/zh-TW.js": "/js/lang/zh-TW.js?id=2160244d8105c946db9b"
+ "/js/lang/de.js": "/js/lang/de.js?id=e0623715e8df0895188b",
+ "/js/lang/en.js": "/js/lang/en.js?id=dce9919ef5fa35e3073a",
+ "/js/lang/fr.js": "/js/lang/fr.js?id=2d1159debd99a1909f12",
+ "/js/lang/ru.js": "/js/lang/ru.js?id=b007ddce75134acbe635",
+ "/js/lang/uk.js": "/js/lang/uk.js?id=146819d3cf1dfb16672d",
+ "/js/lang/zh-TW.js": "/js/lang/zh-TW.js?id=f57574a3892e5990ecbc"
}
diff --git a/includes/discovery/route.inc.php b/includes/discovery/route.inc.php
index 151462c356..46a07001e3 100644
--- a/includes/discovery/route.inc.php
+++ b/includes/discovery/route.inc.php
@@ -14,154 +14,314 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see . */
+//We can use RFC1213 or IP-FORWARD-MIB or MPLS-L3VPN-STD-MIB
-//This file is a litle diferent, because the route depend of the vrf, not of the context,
-//like the others, so if i use the context, you will have the same information n time the context how have the same VRF
-global $debug;
+use App\Models\Device;
+use LibreNMS\Util\IPv4;
+$ipForwardMibRoutesNumber = snmp_get($device, 'IP-FORWARD-MIB::inetCidrRouteNumber.0', '-Osqn');
-$ids = array();
+$ipForwardNb = snmp_get_multi($device, ['inetCidrRouteNumber.0', 'ipCidrRouteNumber.0'], '-OQUs', 'IP-FORWARD-MIB');
-// For the moment only will be cisco and the version 3
-if ($device['os_group'] == "cisco") {
+//Get the configured max routes number
+$max_routes = 1000;
+if (null != (Config::get('routes_max_number'))) {
+ $max_routes = Config::get('routes_max_number');
+}
+
+//Init update/create tables;
+$create_row = [];
+$update_row = [];
+$delete_row = [];
+
+//store timestamp so all update / creation will be synced on same timestamp
+$update_timestamp = dbFetchRows('select now() as now')[0]['now'];
+
+//Load current DB entries:
+$dbRoute = dbFetchRows('select * from `route` where `device_id` = ?', array($device['device_id']));
+foreach ($dbRoute as $dbRow) {
+ $current = $mixed[$dbRow['context_name']][$dbRow['inetCidrRouteDestType']][$dbRow['inetCidrRouteDest']][$dbRow['inetCidrRoutePfxLen']][$dbRow['inetCidrRoutePolicy']][$dbRow['inetCidrRouteNextHopType']][$dbRow['inetCidrRouteNextHop']];
+ if (isset($current) && isset($current['db']) && count($current['db']) > 0) {
+ //We have duplicate routes in DB, we'll clean that.
+ $delete_row[$dbRow['route_id']] = 1;
+ $delete_row_data[$dbRow['route_id']] = $dbRow; //DEBUG DATA ONLY
+ } else {
+ $mixed[$dbRow['context_name']][$dbRow['inetCidrRouteDestType']][$dbRow['inetCidrRouteDest']][$dbRow['inetCidrRoutePfxLen']][$dbRow['inetCidrRoutePolicy']][$dbRow['inetCidrRouteNextHopType']][$dbRow['inetCidrRouteNextHop']]['db'] = $dbRow;
+ }
+}
+
+//Not a single route will be discovered if the amount is over maximum
+// To prevent any bad behaviour on routers holding the full internet table
+
+//if the device does not support IP-FORWARD-MIB, we can still discover the ipv4 (only)
+//routes using RFC1213 but no way to limit the amount of routes here !!
+
+if (! isset($ipForwardNb['0']['inetCidrRouteNumber'])) {
//RFC1213-MIB
$mib = "RFC1213-MIB";
- //IpRouteEntry
- $vrfs_lite_cisco = array();
-
- if (key_exists('vrf_lite_cisco', $device) && (count($device['vrf_lite_cisco']) != 0)) {
- //i will only get one context of vrf, read the begin of this file
- foreach ($device['vrf_lite_cisco'] as $vrf_lite) {
- if (!key_exists($vrf_lite['vrf_name'], $vrfs_lite_cisco)) {
- $vrfs_lite_cisco[$vrf_lite['vrf_name']] = $vrf_lite;
- }
- }
- } else {
- $vrfs_lite_cisco = array(array('context_name' => null));
- }
-
$tableRoute = array();
- foreach ($vrfs_lite_cisco as $vrf_lite) {
- $device['context_name'] = $vrf_lite['context_name'];
-
- ////////////////ipRouteDest//////////////////
- $oid = '.1.3.6.1.2.1.4.21.1.1';
- $resultHelp = snmp_walk($device, $oid, "-Osqn", $mib, null);
- $resultHelp = trim($resultHelp);
- $resultHelp = str_replace("$oid.", "", $resultHelp);
-
- foreach (explode("\n", $resultHelp) as $ipRouteDest) {
- list($ip, $value) = explode(" ", $ipRouteDest);
- $tableRoute[$ip]['ipRouteDest'] = $value;
+ $oid = '.1.3.6.1.2.1.4.21';
+ $ipRoute = snmpwalk_group($device, $oid, $mib, 1, []);
+ d_echo($res);
+ d_echo('Table routage');
+ d_echo($ipRoute);
+ echo "RFC1213 ";
+ foreach ($tableRoute as $ipRoute) {
+ if (empty($ipRoute['ipRouteDest']) || $ipRoute['ipRouteDest'] == '') {
+ continue;
}
- /////////////////ipRouteIfIndex//////////////
- $oid = '.1.3.6.1.2.1.4.21.1.2';
- $resultHelp = snmp_walk($device, $oid, "-Osqn", $mib, null);
- $resultHelp = trim($resultHelp);
- $resultHelp = str_replace("$oid.", "", $resultHelp);
-
- foreach (explode("\n", $resultHelp) as $ipRouteIfIndex) {
- list($ip, $value) = explode(" ", $ipRouteIfIndex);
- $tableRoute[$ip]['ipRouteIfIndex'] = $value;
+ unset($entryClean);
+ $entryClean['inetCidrRouteDestType'] = 'ipv4';
+ $entryClean['inetCidrRouteDest'] = $ipRoute['ipRouteDest'];
+ $inetCidrRoutePfxLen = IPv4::netmask2cidr($ipRoute['ipRouteMask']); //CONVERT
+ $entryClean['inetCidrRoutePfxLen'] = $inetCidrRoutePfxLen;
+ $entryClean['inetCidrRoutePolicy'] = $ipRoute['ipRouteInfo'];
+ $entryClean['inetCidrRouteNextHopType'] = 'ipv4';
+ $entryClean['inetCidrRouteNextHop'] = $ipRoute['ipRouteNextHop'];
+ $entryClean['inetCidrRouteMetric1'] = $ipRoute['ipRouteMetric1'];
+ $entryClean['inetCidrRouteNextHopAS'] = '0';
+ $entryClean['inetCidrRouteProto'] = $ipRoute['ipRouteProto'];
+ $entryClean['inetCidrRouteType'] = $ipRoute['ipRouteType'];
+ $entryClean['inetCidrRouteIfIndex'] = $ipRoute['ipRouteIfIndex'];
+ $entryClean['context_name'] = '';
+ $entryClean['device_id'] = $device['device_id'];
+ $entryClean['port_id'] = Device::find($device['device_id'])->ports()->where('ifIndex', '=', $entryClean['inetCidrRouteIfIndex'])->first()->port_id;
+ $entryClean['updated_at'] = $update_timestamp;
+ $current = $mixed['']['ipv4'][$inetCidrRouteDest][$inetCidrRoutePfxLen][$entryClean['inetCidrRoutePolicy']]['ipv4'][$inetCidrRouteNextHop];
+ if (isset($current) && isset($current['db']) && count($current['db']) > 0 && $delete_row[$current['db']['route_id']] != 1) {
+ //we already have a row in DB
+ $entryClean['route_id'] = $current['db']['route_id'];
+ $update_row[] = $entryClean;
+ } else {
+ $entry['created_at'] = array('NOW()');
+ $create_row[] = $entryClean;
}
+ }
+}
- ///////////////ipRouteMetric1///////////////
- $oid = '.1.3.6.1.2.1.4.21.1.3';
- $resultHelp = snmp_walk($device, $oid, "-Osqn", $mib, null);
- $resultHelp = trim($resultHelp);
- $resultHelp = str_replace("$oid.", "", $resultHelp);
+// Not a single route will be discovered if the amount is over maximum
+// To prevent any bad behaviour on routers holding the full internet table
- foreach (explode("\n", $resultHelp) as $ipRouteMetric) {
- list($ip, $value) = explode(" ", $ipRouteMetric);
- $tableRoute[$ip]['ipRouteMetric'] = $value;
- }
-
- ////////////ipRouteNextHop//////////////////
- $oid = '.1.3.6.1.2.1.4.21.1.7';
- $resultHelp = snmp_walk($device, $oid, "-Osqn", $mib, null);
- $resultHelp = trim($resultHelp);
- $resultHelp = str_replace("$oid.", "", $resultHelp);
- foreach (explode("\n", $resultHelp) as $ipRouteNextHop) {
- list($ip, $value) = explode(" ", $ipRouteNextHop);
- $tableRoute[$ip]['ipRouteNextHop'] = $value;
- }
-
- ////////////ipRouteType/////////////////////
- $oid = '.1.3.6.1.2.1.4.21.1.8';
- $resultHelp = snmp_walk($device, $oid, "-Osqn", $mib, null);
- $resultHelp = trim($resultHelp);
- $resultHelp = str_replace("$oid.", "", $resultHelp);
-
- foreach (explode("\n", $resultHelp) as $ipRouteType) {
- list($ip, $value) = explode(" ", $ipRouteType);
- $tableRoute[$ip]['ipRouteType'] = $value;
- }
-
- ///////////ipRouteProto//////////////////////
- $oid = '.1.3.6.1.2.1.4.21.1.9';
- $resultHelp = snmp_walk($device, $oid, "-Osqn", $mib, null);
- $resultHelp = trim($resultHelp);
- $resultHelp = str_replace("$oid.", "", $resultHelp);
+// IP-FORWARD-MIB with inetCidrRouteTable
- foreach (explode("\n", $resultHelp) as $ipRouteProto) {
- list($ip, $value) = explode(" ", $ipRouteProto);
- $tableRoute[$ip]['ipRouteProto'] = $value;
- }
-
- /*
- ///////////ipRouteAge//////////////////////
- $oid = '.1.3.6.1.2.1.4.21.1.10';
- $resultHelp = snmp_walk($device, $oid, "-Osqn", $mib, NULL);
- $resultHelp = str_replace("$oid.", "", $resultHelp);
-
- foreach (explode("\n", $resultHelp) as $ipRouteAge) {
- list($ip,$value)=explode(" ",$ipRouteAge);
- $tableRoute[$ip]['ipRouteAge']=$value;
- } */
-
- ///////////ipRouteMask//////////////////////
- $oid = '.1.3.6.1.2.1.4.21.1.11';
- $resultHelp = snmp_walk($device, $oid, ['-Osq', '-Ln'], $mib, null);
- $resultHelp = trim($resultHelp);
- $resultHelp = str_replace("$oid.", "", $resultHelp);
-
- foreach (explode("\n", $resultHelp) as $ipRouteMask) {
- list($ip, $value) = explode(" ", $ipRouteMask);
- $tableRoute[$ip]['ipRouteMask'] = $value;
- }
-
- if ($debug) {
- echo 'Table routage';
- var_dump($tableRoute);
- }
-
- foreach ($tableRoute as $ipRoute) {
- if (empty($ipRoute['ipRouteDest']) || $ipRoute['ipRouteDest'] == '') {
- continue;
- }
-
- $oldRouteRow = dbFetchRow('select * from route where device_id = ? AND ipRouteDest = ? AND context_name = ?', array($device['device_id'], $ipRoute['ipRouteDest'], $device['context_name']));
- if (!empty($oldRouteRow)) {
- unset($oldRouteRow['discoveredAt']);
- $changeRoute = array();
- foreach ($ipRoute as $key => $value) {
- if ($oldRouteRow[$key] != $value) {
- $changeRoute[$key] = $value;
+if (isset($ipForwardNb['0']['inetCidrRouteNumber']) && $ipForwardNb['0']['inetCidrRouteNumber'] < $max_routes) {
+ // We have ip forward mib available
+ d_echo('IP FORWARD MIB (with inetCidr support)');
+ $mib = 'IP-FORWARD-MIB';
+ $oid = '.1.3.6.1.2.1.4.24.7.1';
+ $res = snmpwalk_group($device, $oid, $mib, 6, []);
+ $ipForwardNb['0']['inetCidrRouteNumber'] = count($res); // Some cisco devices report ipv4+ipv6 but only include ipv6 in this table
+ echo "inetCidrRoute ";
+ foreach ($res as $inetCidrRouteDestType => $next1) {
+ //ipv4 or ipv6
+ foreach ($next1 as $inetCidrRouteDest => $next2) {
+ //we have only 1 child here, the mask
+ $inetCidrRoutePfxLen = array_keys($next2)[0];
+ $next3 = array_values($next2)[0];
+ $inetCidrRoutePolicy = array_keys($next3)[0];
+ $next4 = array_values($next3)[0];
+ foreach ($next4 as $inetCidrRouteNextHopType => $next5) {
+ foreach ($next5 as $inetCidrRouteNextHop => $entry) {
+ $entry['inetCidrRouteDestType'] = $inetCidrRouteDestType;
+ $entry['inetCidrRouteDest'] = normalize_snmp_ip_address($inetCidrRouteDest);
+ $entry['inetCidrRoutePfxLen'] = $inetCidrRoutePfxLen;
+ $entry['inetCidrRoutePolicy'] = $inetCidrRoutePolicy;
+ $entry['inetCidrRouteNextHopType'] = $inetCidrRouteNextHopType;
+ $entry['inetCidrRouteNextHop'] = normalize_snmp_ip_address($inetCidrRouteNextHop);
+ $entry['context_name'] = '';
+ $entry['device_id'] = $device['device_id'];
+ $entry['port_id'] = Device::find($device['device_id'])->ports()->where('ifIndex', '=', $entry['inetCidrRouteIfIndex'])->first()->port_id;
+ $entry['updated_at'] = $update_timestamp;
+ unset($entry['inetCidrRouteAge']);
+ unset($entry['inetCidrRouteMetric2']);
+ unset($entry['inetCidrRouteMetric3']);
+ unset($entry['inetCidrRouteMetric4']);
+ unset($entry['inetCidrRouteMetric5']);
+ unset($entry['inetCidrRouteStatus']);
+ $entryPerType[$inetCidrRouteDestType]++;
+ $current = $mixed[''][$inetCidrRouteDestType][$inetCidrRouteDest][$inetCidrRoutePfxLen][$inetCidrRoutePolicy][$inetCidrRouteNextHopType][$inetCidrRouteNextHop];
+ if (isset($current) && isset($current['db']) && count($current['db']) > 0 && $delete_row[$current['db']['route_id']] != 1) {
+ //we already have a row in DB
+ $entry['route_id'] = $current['db']['route_id'];
+ $update_row[] = $entry;
+ } else {
+ d_echo(isset($current));
+ d_echo(isset($current['db']));
+ d_echo($current['db']);
+ d_echo(count($current['db']));
+ d_echo($delete_row[$current['db']['route_id']]);
+ $entry['created_at'] = array('NOW()');
+ $create_row[] = $entry;
}
}
- if (!empty($changeRoute)) {
- dbUpdate($changeRoute, 'route', 'device_id = ? and ipRouteDest = ? and context_name = ?', array($device['device_id'], $ipRoute['ipRouteDest'], $device['context_name']));
- }
- } else {
- $toInsert = array_merge($ipRoute, array('device_id' => $device['device_id'], 'context_name' => $device['context_name'], 'discoveredAt' => time()));
- dbInsert($toInsert, 'route');
}
}
- // unset($tableRoute);
}
- unset($vrfs_lite_cisco);
+ $ipForwardNb['0']['inetCidrRouteNumber'] = $entryPerType['ipv4'];
+ // Some cisco devices report ipv4+ipv6 in inetCidrRouteNumber
+ // But only include ipv6 in inetCidrRoute
+ // So we count the real amount of ipv4 we get, in order to get the missing ipv4 from ipCidrRouteTable if needed
}
+
+// IP-FORWARD-MIB with ipCidrRouteTable in case ipCidrRouteTable has more entries than inetCidrRouteTable (Some older routers)
+
+if (isset($ipForwardNb['0']['ipCidrRouteNumber']) && $ipForwardNb['0']['ipCidrRouteNumber'] > $ipForwardNb['0']['inetCidrRouteNumber'] && $ipForwardNb['0']['ipCidrRouteNumber'] < $max_routes) {
+ //device uses only ipCidrRoute and not inetCidrRoute
+ d_echo('IP FORWARD MIB (without inetCidr support)');
+ $mib = 'IP-FORWARD-MIB';
+ $oid = '.1.3.6.1.2.1.4.24.4.1';
+ $ipCidrTable = snmpwalk_group($device, $oid, $mib, 6, []);
+ echo "ipCidrRouteTable ";
+ // we need to translate the values to inetCidr structure;
+ //d_echo($ipCidrTable);
+ foreach ($ipCidrTable as $inetCidrRouteDest => $next1) {
+ foreach ($next1 as $ipCidrRouteMask => $next2) {
+ foreach ($next2 as $ipCidrRouteTos => $next3) {
+ foreach ($next3 as $inetCidrRouteNextHop => $entry) {
+ unset($entryClean);
+ $entryClean['inetCidrRouteDestType'] = 'ipv4';
+ $entryClean['inetCidrRouteDest'] = $inetCidrRouteDest;
+ $inetCidrRoutePfxLen = IPv4::netmask2cidr($entry['ipCidrRouteMask']); //CONVERT
+ $entryClean['inetCidrRoutePfxLen'] = $inetCidrRoutePfxLen;
+ $entryClean['inetCidrRoutePolicy'] = $entry['ipCidrRouteInfo'];
+ $entryClean['inetCidrRouteNextHopType'] = 'ipv4';
+ $entryClean['inetCidrRouteNextHop'] = $inetCidrRouteNextHop;
+ $entryClean['inetCidrRouteMetric1'] = $entry['ipCidrRouteMetric1'];
+ $entryClean['inetCidrRouteProto'] = $entry['ipCidrRouteProto'];
+ $entryClean['inetCidrRouteType'] = $entry['ipCidrRouteType'];
+ $entryClean['inetCidrRouteIfIndex'] = $entry['ipCidrRouteIfIndex'];
+ $entryClean['inetCidrRouteNextHopAS'] = $entry['ipCidrRouteNextHopAS'];
+ $entryClean['context_name'] = '';
+ $entryClean['device_id'] = $device['device_id'];
+ $entryClean['port_id'] = Device::find($device['device_id'])->ports()->where('ifIndex', '=', $entryClean['inetCidrRouteIfIndex'])->first()->port_id;
+ $entryClean['updated_at'] = $update_timestamp;
+ $current = $mixed['']['ipv4'][$inetCidrRouteDest][$inetCidrRoutePfxLen][$entryClean['inetCidrRoutePolicy']]['ipv4'][$inetCidrRouteNextHop];
+ if (isset($current) && isset($current['db']) && count($current['db']) > 0 && $delete_row[$current['db']['route_id']] != 1) {
+ //we already have a row in DB
+ $entryClean['route_id'] = $current['db']['route_id'];
+ $update_row[] = $entryClean;
+ } else {
+ $entryClean['created_at'] = array('NOW()');
+ $create_row[] = $entryClean;
+ }
+ }
+ }
+ }
+ }
+}
+
+// We can now check if we have MPLS VPN routing table available :
+// MPLS-L3VPN-STD-MIB::mplsL3VpnVrfRteTable
+// Route numbers : MPLS-L3VPN-STD-MIB::mplsL3VpnVrfPerfCurrNumRoutes
+
+
+$mib = 'MPLS-L3VPN-STD-MIB';
+$oid = 'mplsL3VpnVrfPerfCurrNumRoutes';
+$mpls_vpn_route_nb = snmpwalk_group($device, $oid, $mib, 6, []);
+
+foreach ($mpls_vpn_route_nb as $vpnId => $route_nb) {
+ if ($route_nb['mplsL3VpnVrfPerfCurrNumRoutes'] > $max_routes) {
+ echo("Skipping all MPLS routes because vpn instance $vpnId has more than $max_routes routes.");
+ $mpls_skip = 1;
+ }
+}
+
+if ($mpls_skip != 1) {
+ echo "mplsL3VpnVrfRteTable ";
+ // We can discover the routes;
+ $oid = 'mplsL3VpnVrfRteTable';
+ $mpls_route_table = snmpwalk_group($device, $oid, $mib, 7, []);
+ foreach ($mpls_route_table as $vpnId => $inetCidrRouteTable) {
+ foreach ($inetCidrRouteTable as $inetCidrRouteDestType => $next1) {
+ //ipv4 or ipv6
+ foreach ($next1 as $inetCidrRouteDest => $next2) {
+ //we have only 1 child here, the mask
+ $inetCidrRoutePfxLen = array_keys($next2)[0];
+ $next3 = array_values($next2)[0];
+ $inetCidrRoutePolicy = array_keys($next3)[0];
+ $next4 = array_values($next3)[0];
+ foreach ($next4 as $inetCidrRouteNextHopType => $next5) {
+ foreach ($next5 as $inetCidrRouteNextHop => $entry) {
+ $entry['inetCidrRouteDestType'] = $inetCidrRouteDestType;
+ $entry['inetCidrRouteDest'] = normalize_snmp_ip_address($inetCidrRouteDest);
+ $entry['inetCidrRoutePfxLen'] = $inetCidrRoutePfxLen;
+ $entry['inetCidrRoutePolicy'] = $inetCidrRoutePolicy;
+ $entry['inetCidrRouteNextHopType'] = $inetCidrRouteNextHopType;
+ $entry['inetCidrRouteNextHop'] = normalize_snmp_ip_address($inetCidrRouteNextHop);
+ $entry['context_name'] = $vpnId;
+ $entry['device_id'] = $device['device_id'];
+ $entry['port_id'] = Device::find($device['device_id'])->ports()->where('ifIndex', '=', $entry['inetCidrRouteIfIndex'])->first()->port_id;
+ $entry['updated_at'] = $update_timestamp;
+ $entry['inetCidrRouteIfIndex'] = $entry['mplsL3VpnVrfRteInetCidrIfIndex'];
+ $entry['inetCidrRouteType'] = $entry['mplsL3VpnVrfRteInetCidrType'];
+ $entry['inetCidrRouteProto'] = $entry['mplsL3VpnVrfRteInetCidrProto'];
+ $entry['inetCidrRouteMetric1'] = $entry['mplsL3VpnVrfRteInetCidrMetric1'];
+ $entry['inetCidrRouteNextHopAS'] = $entry['mplsL3VpnVrfRteInetCidrNextHopAS'];
+ unset($entry['mplsL3VpnVrfRteXCPointer']);
+ unset($entry['mplsL3VpnVrfRteInetCidrMetric1']);
+ unset($entry['mplsL3VpnVrfRteInetCidrMetric2']);
+ unset($entry['mplsL3VpnVrfRteInetCidrMetric3']);
+ unset($entry['mplsL3VpnVrfRteInetCidrMetric4']);
+ unset($entry['mplsL3VpnVrfRteInetCidrMetric5']);
+ unset($entry['mplsL3VpnVrfRteInetCidrAge']);
+ unset($entry['mplsL3VpnVrfRteInetCidrProto']);
+ unset($entry['mplsL3VpnVrfRteInetCidrType']);
+ unset($entry['mplsL3VpnVrfRteInetCidrStatus']);
+ unset($entry['mplsL3VpnVrfRteInetCidrIfIndex']);
+ unset($entry['mplsL3VpnVrfRteInetCidrNextHopAS']);
+ $current = $mixed[$vpnId][$inetCidrRouteDestType][$inetCidrRouteDest][$inetCidrRoutePfxLen][$inetCidrRoutePolicy][$inetCidrRouteNextHopType][$inetCidrRouteNextHop];
+ if (isset($current) && isset($current['db']) && count($current['db']) > 0 && $delete_row[$current['db']['route_id']] != 1) {
+ //we already have a row in DB
+ $entry['route_id'] = $current['db']['route_id'];
+ $update_row[] = $entry;
+ } else {
+ d_echo(isset($current));
+ d_echo(isset($current['db']));
+ d_echo($current['db']);
+ d_echo(count($current['db']));
+ d_echo($delete_row[$current['db']['route_id']]);
+ $entry['created_at'] = array('NOW()');
+ $create_row[] = $entry;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+echo "\nProcessing: ";
+
+// We can now process the data into the DB
+foreach ($delete_row as $k => $v) {
+ if ($v > 0) {
+ dbDelete(
+ 'route',
+ '`route_id` = ?',
+ array($k)
+ );
+ echo '-';
+ d_echo($delete_row_data[$k]);
+ }
+}
+
+foreach ($update_row as $upd_entry) {
+ dbUpdate(
+ $upd_entry,
+ 'route',
+ '`route_id` = ?',
+ array($upd_entry['route_id'])
+ );
+ echo '.';
+}
+
+foreach ($create_row as $new_entry) {
+ $new_entry['created_at'] = $update_timestamp;
+ dbInsert($new_entry, 'route');
+ echo '+';
+}
+
+// EOF
diff --git a/includes/functions.php b/includes/functions.php
index 305abe92a2..1e6b6da5cd 100644
--- a/includes/functions.php
+++ b/includes/functions.php
@@ -1323,6 +1323,14 @@ function convert_delay($delay)
return($delay_sec);
}
+function normalize_snmp_ip_address($data)
+{
+ // $data is received from snmpwalk, can be ipv4 xxx.xxx.xxx.xxx or ipv6 xx:xx:...:xx (16 chunks)
+ // ipv4 is returned unchanged, ipv6 is returned with one ':' removed out of two, like
+ // xxxx:xxxx:...:xxxx (8 chuncks)
+ return (preg_replace('/([0-9a-fA-F]{2}):([0-9a-fA-F]{2})/', '\1\2', explode('%', $data, 2)[0]));
+}
+
function guidv4($data)
{
// http://stackoverflow.com/questions/2040240/php-function-to-generate-v4-uuid#15875555
diff --git a/includes/html/pages/device.inc.php b/includes/html/pages/device.inc.php
index 9184878de3..a3a0c78b67 100644
--- a/includes/html/pages/device.inc.php
+++ b/includes/html/pages/device.inc.php
@@ -269,6 +269,11 @@ if (device_permitted($vars['device']) || $permitted_by_port) {
$routing_tabs[] = 'cisco-otv';
}
+ $device_routing_count['routes'] = dbFetchCell('SELECT COUNT(*) FROM `route` WHERE `device_id` = ?', array($device['device_id']));
+ if ($device_routing_count['routes']) {
+ $routing_tabs[] = 'routes';
+ }
+
if (is_array($routing_tabs)) {
echo '
diff --git a/includes/html/pages/device/routing.inc.php b/includes/html/pages/device/routing.inc.php
index c0ffdaf547..c01e884d70 100644
--- a/includes/html/pages/device/routing.inc.php
+++ b/includes/html/pages/device/routing.inc.php
@@ -20,6 +20,7 @@ $type_text['bgp'] = 'BGP';
$type_text['cef'] = 'CEF';
$type_text['ospf'] = 'OSPF';
$type_text['vrf'] = 'VRFs';
+$type_text['routes'] = 'Routing Table';
$type_text['cisco-otv'] = 'OTV';
$type_text['mpls'] = 'MPLS';
diff --git a/includes/html/pages/device/routing/routes.inc.php b/includes/html/pages/device/routing/routes.inc.php
new file mode 100644
index 0000000000..6e33774875
--- /dev/null
+++ b/includes/html/pages/device/routing/routes.inc.php
@@ -0,0 +1,54 @@
+
+
+
+
+ VRF |
+ Proto |
+ Destination |
+ Mask |
+ Next hop |
+ Interface |
+ Metric |
+ Type |
+ Proto |
+ First seen |
+ Last seen |
+
+
+
+Warning: Routing Table is only retrieved during device discovery. Devices are skipped if they have more than routes.
+
diff --git a/misc/config_definitions.json b/misc/config_definitions.json
index 8fb090d999..f62938583f 100644
--- a/misc/config_definitions.json
+++ b/misc/config_definitions.json
@@ -3878,6 +3878,20 @@
},
"type": "array"
},
+ "routes_max_number": {
+ "default": 1000,
+ "group": "discovery",
+ "section": "route",
+ "order": 3,
+ "type": "integer"
+ },
+ "route_purge": {
+ "default": 10,
+ "group": "system",
+ "section": "cleanup",
+ "order": 2,
+ "type": "integer"
+ },
"rrd.heartbeat": {
"default": 600,
"group": "poller",
diff --git a/misc/db_schema.yaml b/misc/db_schema.yaml
index c6d44d8c1e..afa066d05f 100644
--- a/misc/db_schema.yaml
+++ b/misc/db_schema.yaml
@@ -1552,18 +1552,25 @@ pseudowires:
PRIMARY: { Name: PRIMARY, Columns: [pseudowire_id], Unique: true, Type: BTREE }
route:
Columns:
+ - { Field: route_id, Type: 'int(10) unsigned', 'Null': false, Extra: auto_increment }
+ - { Field: created_at, Type: timestamp, 'Null': true, Extra: '' }
+ - { Field: updated_at, Type: timestamp, 'Null': true, Extra: '' }
- { Field: device_id, Type: 'int(10) unsigned', 'Null': false, Extra: '' }
- - { Field: context_name, Type: varchar(128), 'Null': false, Extra: '' }
- - { Field: ipRouteDest, Type: varchar(39), 'Null': false, Extra: '' }
- - { Field: ipRouteIfIndex, Type: varchar(256), 'Null': true, Extra: '' }
- - { Field: ipRouteMetric, Type: varchar(256), 'Null': false, Extra: '' }
- - { Field: ipRouteNextHop, Type: varchar(39), 'Null': false, Extra: '' }
- - { Field: ipRouteType, Type: varchar(256), 'Null': false, Extra: '' }
- - { Field: ipRouteProto, Type: varchar(256), 'Null': false, Extra: '' }
- - { Field: discoveredAt, Type: 'int(10) unsigned', 'Null': false, Extra: '' }
- - { Field: ipRouteMask, Type: varchar(256), 'Null': false, Extra: '' }
+ - { Field: port_id, Type: 'int(10) unsigned', 'Null': false, Extra: '' }
+ - { Field: context_name, Type: varchar(255), 'Null': true, Extra: '' }
+ - { Field: inetCidrRouteIfIndex, Type: bigint(20), 'Null': false, Extra: '' }
+ - { Field: inetCidrRouteType, Type: 'int(10) unsigned', 'Null': false, Extra: '' }
+ - { Field: inetCidrRouteProto, Type: 'int(10) unsigned', 'Null': false, Extra: '' }
+ - { Field: inetCidrRouteNextHopAS, Type: 'int(10) unsigned', 'Null': false, Extra: '' }
+ - { Field: inetCidrRouteMetric1, Type: 'int(10) unsigned', 'Null': false, Extra: '' }
+ - { Field: inetCidrRouteDestType, Type: varchar(255), 'Null': false, Extra: '' }
+ - { Field: inetCidrRouteDest, Type: varchar(255), 'Null': false, Extra: '' }
+ - { Field: inetCidrRouteNextHopType, Type: varchar(255), 'Null': false, Extra: '' }
+ - { Field: inetCidrRouteNextHop, Type: varchar(255), 'Null': false, Extra: '' }
+ - { Field: inetCidrRoutePolicy, Type: varchar(255), 'Null': false, Extra: '' }
+ - { Field: inetCidrRoutePfxLen, Type: 'int(10) unsigned', 'Null': false, Extra: '' }
Indexes:
- device: { Name: device, Columns: [device_id, context_name, ipRouteDest, ipRouteNextHop], Unique: false, Type: BTREE }
+ PRIMARY: { Name: PRIMARY, Columns: [route_id], Unique: true, Type: BTREE }
sensors:
Columns:
- { Field: sensor_id, Type: 'int(10) unsigned', 'Null': false, Extra: auto_increment }
diff --git a/resources/lang/en/settings.php b/resources/lang/en/settings.php
index 43d46b60f2..30633dd31e 100644
--- a/resources/lang/en/settings.php
+++ b/resources/lang/en/settings.php
@@ -24,7 +24,8 @@ return [
'ldap' => 'LDAP Settings'
],
'discovery' => [
- 'general' => 'General Discovery Settings'
+ 'general' => 'General Discovery Settings',
+ 'route' => 'Routes Discovery Module',
],
'external' => [
'binaries' => 'Binary Locations',
@@ -596,6 +597,14 @@ return [
'description' => 'Show status publicly',
'help' => 'Shows the status of some devices on the logon page without authentication.'
],
+ 'routes_max_number' => [
+ 'description' => 'Max number of routes allowed for discovery',
+ 'help' => 'No route will be discovered if the size of the routing table is bigger than this number'
+ ],
+ 'route_purge' => [
+ 'description' => 'Route entries older than (days)',
+ 'help' => 'Cleanup done by daily.sh'
+ ],
'rrd' => [
'heartbeat' => [
'description' => 'Change the rrd heartbeat value (default 600)'
diff --git a/routes/web.php b/routes/web.php
index 7259df32df..b9cd85c4cf 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -102,6 +102,7 @@ Route::group(['middleware' => ['auth', '2fa'], 'guard' => 'auth'], function () {
Route::post('device', 'DeviceController');
Route::post('eventlog', 'EventlogController');
Route::post('fdb-tables', 'FdbTablesController');
+ Route::post('routes', 'RoutesTablesController');
Route::post('graylog', 'GraylogController');
Route::post('location', 'LocationController');
Route::post('port-nac', 'PortNacController');
diff --git a/tests/module_tables.yaml b/tests/module_tables.yaml
index 11c7105058..c0a644ee4a 100644
--- a/tests/module_tables.yaml
+++ b/tests/module_tables.yaml
@@ -61,6 +61,10 @@ ports:
joins:
- { left: ports.port_id, right: ports_statistics.port_id }
order_by: ports.ifIndex, ports.ifDescr, ports.ifName
+route:
+ route:
+ excluded_fields: [device_id, route_id, created_at, updated_at]
+ order_by: inetCidrRouteDest
nac:
ports_nac:
excluded_fields: [ports_nac_id, device_id, port_id]
diff --git a/tests/snmpsim/vrp_route.snmprec b/tests/snmpsim/vrp_route.snmprec
new file mode 100644
index 0000000000..49196256f6
--- /dev/null
+++ b/tests/snmpsim/vrp_route.snmprec
@@ -0,0 +1,152 @@
+1.3.6.1.2.1.1.1.0|4x|53353732302d3536432d5057522d45492d41430a48756177656920566572736174696c6520526f7574696e6720506c6174666f726d20536f667477617265200d0a205652502028522920736f6674776172652c56657273696f6e20352e3137302028533537323020563230305230313143313053504336303029200d0a20436f707972696768742028432920323030372048756177656920546563686e6f6c6f6769657320436f2e2c204c74642e
+1.3.6.1.2.1.1.2.0|6|1.3.6.1.4.1.2011.2.23.291
+1.3.6.1.2.1.1.3.0|67|103903498
+1.3.6.1.2.1.1.4.0|4|
+1.3.6.1.2.1.1.5.0|4|
+1.3.6.1.2.1.1.6.0|4|
+1.3.6.1.2.1.4.24.3.0|66|5
+1.3.6.1.2.1.4.24.6.0|66|5
+1.3.6.1.2.1.4.24.7.1.7.1.4.0.0.0.0.0.2.0.0.1.4.10.199.0.1|2|58
+1.3.6.1.2.1.4.24.7.1.7.1.4.10.101.155.0.24.2.0.0.1.4.10.199.0.1|2|118
+1.3.6.1.2.1.4.24.7.1.7.1.4.10.120.0.0.24.2.0.0.1.4.10.199.0.1|2|118
+1.3.6.1.2.1.4.24.7.1.7.1.4.10.121.2.0.24.2.0.0.1.4.10.199.0.1|2|118
+1.3.6.1.2.1.4.24.7.1.7.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.50|2|58
+1.3.6.1.2.1.4.24.7.1.7.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.103|2|118
+1.3.6.1.2.1.4.24.7.1.7.1.4.10.199.0.50.32.2.0.0.1.4.127.0.0.1|2|58
+1.3.6.1.2.1.4.24.7.1.7.1.4.10.199.0.103.32.2.0.0.1.4.127.0.0.1|2|118
+1.3.6.1.2.1.4.24.7.1.7.1.4.10.199.0.255.32.2.0.0.1.4.127.0.0.1|2|118
+1.3.6.1.2.1.4.24.7.1.7.1.4.127.0.0.0.8.2.0.0.1.4.127.0.0.1|2|1
+1.3.6.1.2.1.4.24.7.1.7.1.4.127.0.0.1.32.2.0.0.1.4.127.0.0.1|2|1
+1.3.6.1.2.1.4.24.7.1.7.1.4.127.255.255.255.32.2.0.0.1.4.127.0.0.1|2|3
+1.3.6.1.2.1.4.24.7.1.7.1.4.255.255.255.255.32.2.0.0.1.4.127.0.0.1|2|3
+1.3.6.1.2.1.4.24.7.1.8.1.4.0.0.0.0.0.2.0.0.1.4.10.199.0.1|2|4
+1.3.6.1.2.1.4.24.7.1.8.1.4.10.101.155.0.24.2.0.0.1.4.10.199.0.1|2|4
+1.3.6.1.2.1.4.24.7.1.8.1.4.10.120.0.0.24.2.0.0.1.4.10.199.0.1|2|4
+1.3.6.1.2.1.4.24.7.1.8.1.4.10.121.2.0.24.2.0.0.1.4.10.199.0.1|2|4
+1.3.6.1.2.1.4.24.7.1.8.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.50|2|3
+1.3.6.1.2.1.4.24.7.1.8.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.103|2|3
+1.3.6.1.2.1.4.24.7.1.8.1.4.10.199.0.50.32.2.0.0.1.4.127.0.0.1|2|3
+1.3.6.1.2.1.4.24.7.1.8.1.4.10.199.0.103.32.2.0.0.1.4.127.0.0.1|2|3
+1.3.6.1.2.1.4.24.7.1.8.1.4.10.199.0.255.32.2.0.0.1.4.127.0.0.1|2|3
+1.3.6.1.2.1.4.24.7.1.8.1.4.127.0.0.0.8.2.0.0.1.4.127.0.0.1|2|3
+1.3.6.1.2.1.4.24.7.1.8.1.4.127.0.0.1.32.2.0.0.1.4.127.0.0.1|2|3
+1.3.6.1.2.1.4.24.7.1.8.1.4.127.255.255.255.32.2.0.0.1.4.127.0.0.1|2|3
+1.3.6.1.2.1.4.24.7.1.8.1.4.255.255.255.255.32.2.0.0.1.4.127.0.0.1|2|3
+1.3.6.1.2.1.4.24.7.1.9.1.4.0.0.0.0.0.2.0.0.1.4.10.199.0.1|2|3
+1.3.6.1.2.1.4.24.7.1.9.1.4.10.101.155.0.24.2.0.0.1.4.10.199.0.1|2|3
+1.3.6.1.2.1.4.24.7.1.9.1.4.10.120.0.0.24.2.0.0.1.4.10.199.0.1|2|3
+1.3.6.1.2.1.4.24.7.1.9.1.4.10.121.2.0.24.2.0.0.1.4.10.199.0.1|2|3
+1.3.6.1.2.1.4.24.7.1.9.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.50|2|2
+1.3.6.1.2.1.4.24.7.1.9.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.103|2|2
+1.3.6.1.2.1.4.24.7.1.9.1.4.10.199.0.50.32.2.0.0.1.4.127.0.0.1|2|2
+1.3.6.1.2.1.4.24.7.1.9.1.4.10.199.0.103.32.2.0.0.1.4.127.0.0.1|2|2
+1.3.6.1.2.1.4.24.7.1.9.1.4.10.199.0.255.32.2.0.0.1.4.127.0.0.1|2|2
+1.3.6.1.2.1.4.24.7.1.9.1.4.127.0.0.0.8.2.0.0.1.4.127.0.0.1|2|2
+1.3.6.1.2.1.4.24.7.1.9.1.4.127.0.0.1.32.2.0.0.1.4.127.0.0.1|2|2
+1.3.6.1.2.1.4.24.7.1.9.1.4.127.255.255.255.32.2.0.0.1.4.127.0.0.1|2|2
+1.3.6.1.2.1.4.24.7.1.9.1.4.255.255.255.255.32.2.0.0.1.4.127.0.0.1|2|2
+1.3.6.1.2.1.4.24.7.1.10.1.4.0.0.0.0.0.2.0.0.1.4.10.199.0.1|66|1038806
+1.3.6.1.2.1.4.24.7.1.10.1.4.10.101.155.0.24.2.0.0.1.4.10.199.0.1|66|5477268
+1.3.6.1.2.1.4.24.7.1.10.1.4.10.120.0.0.24.2.0.0.1.4.10.199.0.1|66|5477268
+1.3.6.1.2.1.4.24.7.1.10.1.4.10.121.2.0.24.2.0.0.1.4.10.199.0.1|66|5477268
+1.3.6.1.2.1.4.24.7.1.10.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.50|66|1038806
+1.3.6.1.2.1.4.24.7.1.10.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.103|66|5477268
+1.3.6.1.2.1.4.24.7.1.10.1.4.10.199.0.50.32.2.0.0.1.4.127.0.0.1|66|1038806
+1.3.6.1.2.1.4.24.7.1.10.1.4.10.199.0.103.32.2.0.0.1.4.127.0.0.1|66|5477268
+1.3.6.1.2.1.4.24.7.1.10.1.4.10.199.0.255.32.2.0.0.1.4.127.0.0.1|66|5477268
+1.3.6.1.2.1.4.24.7.1.10.1.4.127.0.0.0.8.2.0.0.1.4.127.0.0.1|66|1038854
+1.3.6.1.2.1.4.24.7.1.10.1.4.127.0.0.1.32.2.0.0.1.4.127.0.0.1|66|1038854
+1.3.6.1.2.1.4.24.7.1.10.1.4.127.255.255.255.32.2.0.0.1.4.127.0.0.1|66|5905686
+1.3.6.1.2.1.4.24.7.1.10.1.4.255.255.255.255.32.2.0.0.1.4.127.0.0.1|66|5905686
+1.3.6.1.2.1.4.24.7.1.11.1.4.0.0.0.0.0.2.0.0.1.4.10.199.0.1|66|0
+1.3.6.1.2.1.4.24.7.1.11.1.4.10.101.155.0.24.2.0.0.1.4.10.199.0.1|66|0
+1.3.6.1.2.1.4.24.7.1.11.1.4.10.120.0.0.24.2.0.0.1.4.10.199.0.1|66|0
+1.3.6.1.2.1.4.24.7.1.11.1.4.10.121.2.0.24.2.0.0.1.4.10.199.0.1|66|0
+1.3.6.1.2.1.4.24.7.1.11.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.50|66|0
+1.3.6.1.2.1.4.24.7.1.11.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.103|66|0
+1.3.6.1.2.1.4.24.7.1.11.1.4.10.199.0.50.32.2.0.0.1.4.127.0.0.1|66|0
+1.3.6.1.2.1.4.24.7.1.11.1.4.10.199.0.103.32.2.0.0.1.4.127.0.0.1|66|0
+1.3.6.1.2.1.4.24.7.1.11.1.4.10.199.0.255.32.2.0.0.1.4.127.0.0.1|66|0
+1.3.6.1.2.1.4.24.7.1.11.1.4.127.0.0.0.8.2.0.0.1.4.127.0.0.1|66|0
+1.3.6.1.2.1.4.24.7.1.11.1.4.127.0.0.1.32.2.0.0.1.4.127.0.0.1|66|0
+1.3.6.1.2.1.4.24.7.1.11.1.4.127.255.255.255.32.2.0.0.1.4.127.0.0.1|66|0
+1.3.6.1.2.1.4.24.7.1.11.1.4.255.255.255.255.32.2.0.0.1.4.127.0.0.1|66|0
+1.3.6.1.2.1.4.24.7.1.12.1.4.0.0.0.0.0.2.0.0.1.4.10.199.0.1|2|0
+1.3.6.1.2.1.4.24.7.1.12.1.4.10.101.155.0.24.2.0.0.1.4.10.199.0.1|2|0
+1.3.6.1.2.1.4.24.7.1.12.1.4.10.120.0.0.24.2.0.0.1.4.10.199.0.1|2|0
+1.3.6.1.2.1.4.24.7.1.12.1.4.10.121.2.0.24.2.0.0.1.4.10.199.0.1|2|0
+1.3.6.1.2.1.4.24.7.1.12.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.50|2|0
+1.3.6.1.2.1.4.24.7.1.12.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.103|2|0
+1.3.6.1.2.1.4.24.7.1.12.1.4.10.199.0.50.32.2.0.0.1.4.127.0.0.1|2|0
+1.3.6.1.2.1.4.24.7.1.12.1.4.10.199.0.103.32.2.0.0.1.4.127.0.0.1|2|0
+1.3.6.1.2.1.4.24.7.1.12.1.4.10.199.0.255.32.2.0.0.1.4.127.0.0.1|2|0
+1.3.6.1.2.1.4.24.7.1.12.1.4.127.0.0.0.8.2.0.0.1.4.127.0.0.1|2|0
+1.3.6.1.2.1.4.24.7.1.12.1.4.127.0.0.1.32.2.0.0.1.4.127.0.0.1|2|0
+1.3.6.1.2.1.4.24.7.1.12.1.4.127.255.255.255.32.2.0.0.1.4.127.0.0.1|2|0
+1.3.6.1.2.1.4.24.7.1.12.1.4.255.255.255.255.32.2.0.0.1.4.127.0.0.1|2|0
+1.3.6.1.2.1.4.24.7.1.13.1.4.0.0.0.0.0.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.13.1.4.10.101.155.0.24.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.13.1.4.10.120.0.0.24.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.13.1.4.10.121.2.0.24.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.13.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.50|2|-1
+1.3.6.1.2.1.4.24.7.1.13.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.103|2|-1
+1.3.6.1.2.1.4.24.7.1.13.1.4.10.199.0.50.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.13.1.4.10.199.0.103.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.13.1.4.10.199.0.255.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.13.1.4.127.0.0.0.8.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.13.1.4.127.0.0.1.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.13.1.4.127.255.255.255.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.13.1.4.255.255.255.255.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.14.1.4.0.0.0.0.0.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.14.1.4.10.101.155.0.24.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.14.1.4.10.120.0.0.24.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.14.1.4.10.121.2.0.24.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.14.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.50|2|-1
+1.3.6.1.2.1.4.24.7.1.14.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.103|2|-1
+1.3.6.1.2.1.4.24.7.1.14.1.4.10.199.0.50.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.14.1.4.10.199.0.103.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.14.1.4.10.199.0.255.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.14.1.4.127.0.0.0.8.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.14.1.4.127.0.0.1.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.14.1.4.127.255.255.255.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.14.1.4.255.255.255.255.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.15.1.4.0.0.0.0.0.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.15.1.4.10.101.155.0.24.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.15.1.4.10.120.0.0.24.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.15.1.4.10.121.2.0.24.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.15.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.50|2|-1
+1.3.6.1.2.1.4.24.7.1.15.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.103|2|-1
+1.3.6.1.2.1.4.24.7.1.15.1.4.10.199.0.50.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.15.1.4.10.199.0.103.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.15.1.4.10.199.0.255.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.15.1.4.127.0.0.0.8.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.15.1.4.127.0.0.1.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.15.1.4.127.255.255.255.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.15.1.4.255.255.255.255.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.16.1.4.0.0.0.0.0.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.16.1.4.10.101.155.0.24.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.16.1.4.10.120.0.0.24.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.16.1.4.10.121.2.0.24.2.0.0.1.4.10.199.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.16.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.50|2|-1
+1.3.6.1.2.1.4.24.7.1.16.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.103|2|-1
+1.3.6.1.2.1.4.24.7.1.16.1.4.10.199.0.50.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.16.1.4.10.199.0.103.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.16.1.4.10.199.0.255.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.16.1.4.127.0.0.0.8.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.16.1.4.127.0.0.1.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.16.1.4.127.255.255.255.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.16.1.4.255.255.255.255.32.2.0.0.1.4.127.0.0.1|2|-1
+1.3.6.1.2.1.4.24.7.1.17.1.4.0.0.0.0.0.2.0.0.1.4.10.199.0.1|2|1
+1.3.6.1.2.1.4.24.7.1.17.1.4.10.101.155.0.24.2.0.0.1.4.10.199.0.1|2|1
+1.3.6.1.2.1.4.24.7.1.17.1.4.10.120.0.0.24.2.0.0.1.4.10.199.0.1|2|1
+1.3.6.1.2.1.4.24.7.1.17.1.4.10.121.2.0.24.2.0.0.1.4.10.199.0.1|2|1
+1.3.6.1.2.1.4.24.7.1.17.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.50|2|1
+1.3.6.1.2.1.4.24.7.1.17.1.4.10.199.0.0.24.2.0.0.1.4.10.199.0.103|2|1
+1.3.6.1.2.1.4.24.7.1.17.1.4.10.199.0.50.32.2.0.0.1.4.127.0.0.1|2|1
+1.3.6.1.2.1.4.24.7.1.17.1.4.10.199.0.103.32.2.0.0.1.4.127.0.0.1|2|1
+1.3.6.1.2.1.4.24.7.1.17.1.4.10.199.0.255.32.2.0.0.1.4.127.0.0.1|2|1
+1.3.6.1.2.1.4.24.7.1.17.1.4.127.0.0.0.8.2.0.0.1.4.127.0.0.1|2|1
+1.3.6.1.2.1.4.24.7.1.17.1.4.127.0.0.1.32.2.0.0.1.4.127.0.0.1|2|1
+1.3.6.1.2.1.4.24.7.1.17.1.4.127.255.255.255.32.2.0.0.1.4.127.0.0.1|2|1
+1.3.6.1.2.1.4.24.7.1.17.1.4.255.255.255.255.32.2.0.0.1.4.127.0.0.1|2|1
+1.3.6.1.6.3.10.2.1.3.0|2|1038865