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
This commit is contained in:
PipoCanaja 2019-11-17 16:30:43 +01:00 committed by GitHub
parent 92d8040a8f
commit bf181b9dc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 902 additions and 151 deletions

View File

@ -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));
}

View File

@ -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);
}
}

View File

@ -0,0 +1,195 @@
<?php
/**
* RoutesTablesController.php
*
* Route tables data for bootgrid display
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @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'] . "<br>(" . 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'] = '<a href="' . Url::generate(['page' => 'routing', 'protocol' => 'vrf', 'vrf' => $route_entry->context_name]) . '">' . $route_entry->context_name . '</a>' ;
}
return $item;
}
}

51
app/Models/Route.php Normal file
View File

@ -0,0 +1,51 @@
<?php
namespace App\Models;
class Route extends DeviceRelatedModel
{
protected $table = 'route';
protected $primaryKey = 'route_id';
public static $translateProto = [
'undefined',
'other',
'local',
'netmgmt',
'icmp',
'egp',
'ggp',
'hello',
'rip',
'isIs',
'esIs',
'ciscoIgrp',
'bbnSpfIgp',
'ospf',
'bgp',
'idpr',
'ciscoEigrp',
'dvmrp'
];
public static $translateType = [
'undefined',
'other',
'reject',
'local',
'remote',
'blackhole',
];
public $timestamps = true;
// ---- Define Relationships ----
public function device()
{
return $this->belongsTo('App\Models\Device', 'device_id', 'device_id');
}
public function port()
{
return $this->belongsTo('App\Models\Port', 'port_id', 'port_id');
}
}

View File

@ -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);

View File

@ -275,6 +275,7 @@ main () {
"alert_log"
"rrd_purge"
"ports_fdb"
"route"
"ports_purge");
call_daily_php "${options[@]}";
;;

View File

@ -0,0 +1,64 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use App\Models\PortsFdb;
use Carbon\Carbon;
class UpdateRouteTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
//Remove the old route table, as it is not used anymore.
Schema::drop('route');
Schema::create('route', function (Blueprint $table) {
$table->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');
});
}
}

View File

@ -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

View File

@ -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"
}

View File

@ -14,154 +14,314 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
//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

View File

@ -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

View File

@ -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 '<li role="presentation" '.$select['routing'].'>
<a href="'.generate_device_url($device, array('tab' => 'routing')).'">

View File

@ -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';

View File

@ -0,0 +1,54 @@
<?php
use Librenms\Config;
$no_refresh = true;
?>
<table id="routes" class="table table-condensed table-hover table-striped">
<thead>
<tr>
<th data-column-id="context_name" data-width="125px">VRF</th>
<th data-column-id="inetCidrRouteDestType" data-width="70px">Proto</th>
<th data-column-id="inetCidrRouteDest">Destination</th>
<th data-column-id="inetCidrRoutePfxLen" data-width="80px">Mask</th>
<th data-column-id="inetCidrRouteNextHop">Next hop</th>
<th data-column-id="inetCidrRouteIfIndex">Interface</th>
<th data-column-id="inetCidrRouteMetric1" data-width="85px">Metric</th>
<th data-column-id="inetCidrRouteType" data-width="85px">Type</th>
<th data-column-id="inetCidrRouteProto" data-width="85px">Proto</th>
<th data-column-id="created_at" data-width="165px">First seen</th>
<th data-column-id="updated_at" data-width="165px">Last seen</th>
</tr>
</thead>
</table>
<div>Warning: Routing Table is only retrieved during device discovery. Devices are skipped if they have more than <?php echo Config::get('routes_max_number');?> routes.</div>
<script>
var grid = $("#routes").bootgrid({
ajax: true,
post: function ()
{
var check_showAllRoutes = document.getElementById('check_showAllRoutes');
if (check_showAllRoutes) {
var showAllRoutes = document.getElementById('check_showAllRoutes').checked;
} else {
var showAllRoutes = false;
}
return {
device_id: "<?php echo $device['device_id']; ?>",
showAllRoutes: showAllRoutes,
};
},
url: "ajax/table/routes"
});
var add = $(".actionBar").append(
'<div class="search form-group pull-left" style="width:auto">' +
'<?php echo csrf_field() ?>'+
'<input type="checkbox" name="check_showAllRoutes" data-size="small" id="check_showAllRoutes">' +
'&nbsp;Include historical routes in the table.' +
'</div>');
$("#check_showAllRoutes").bootstrapSwitch({
'onSwitchChange': function(event, state){
$('#routes').bootgrid('reload');
}
});
</script>

View File

@ -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",

View File

@ -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 }

View File

@ -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)'

View File

@ -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');

View File

@ -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]

View File

@ -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|<private>
1.3.6.1.2.1.1.5.0|4|<private>
1.3.6.1.2.1.1.6.0|4|<private>
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