Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions LibreNMS/Modules/Links.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
use LibreNMS\Util\StringHelpers;
use SnmpQuery;

/// AVOID the dbFacline() legacy function helpers
use Illuminate\Support\Facades\DB;


class Links implements Module
{
use SyncsModels;
Expand Down Expand Up @@ -151,6 +155,13 @@ private function discoverLldp(Device $device): Collection
}

if (! empty($lldpRows)) {

// Create or Update the table ports_lldplocportid with the lldpLocPortId entries for this device,
// along with the LibreNMS device ID and LibreNMS port ID. This makes it possible to look up the
// lldpLocPortId later to make links between device ports that use less standard identifiers (such
// as MAC address)
discoverLldpLocPortId($device);

$oidsremadd = SnmpQuery::hideMib()->numeric()->walk('LLDP-MIB::lldpRemManAddrIfSubtype')->values();
foreach ($oidsremadd as $key => $tmp1) {
$res = preg_match("/1\.0\.8802\.1\.1\.2\.1\.4\.2\.1\.3\.([^\.]*)\.([^\.]*)\.([^\.]*)\.([^\.]*)\.([^\.]*).(([^\.]*)(\.([^\.]*))+)/", (string) $key, $matches);
Expand Down Expand Up @@ -278,11 +289,13 @@ private function discoverLldp(Device $device): Collection

$remoteSysName = StringHelpers::linksRemSysName($data['lldpRemSysName']);
$remoteSysName = (empty($remoteSysName)) ? StringHelpers::linksRemSysName($data['lldpRemSysDesc']) : $remoteSysName;

$remoteDeviceIp = $data['lldpRemManAddr'] ?? '';
$remoteDeviceId = find_device_id($remoteSysName, $remoteDeviceIp, $remoteMac);

$remotePortId = find_port_id($data['lldpRemPortDesc'] ?? null, $data['lldpRemPortId'], $remoteDeviceId);
// Lookup the device ID and port ID by checking the ports_lldpLocPortId table
$remotePortIdByLldpPortId = find_port_id_by_lldp_port_id($data["lldpRemPortId"], $remoteDeviceId);

$remotePortId = $remotePortIdByLldpPortId;
$remotePortName = (empty($remotePortId)) ? $remotePortName . ' (' . $remoteMac . ')' : $remotePortName;

if (! empty($data['localPortId']) && (! empty($remoteSysName))) {
Expand All @@ -299,10 +312,9 @@ private function discoverLldp(Device $device): Collection
'remote_version' => $data['lldpRemSysDesc'] ?? '',
]));
}
}
}
}

return $links->filter();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::create('ports_lldplocportid', function (Blueprint $table) {
$table->unsignedInteger('device_id');
$table->string('lldpLocPortId');
$table->unsignedInteger('port_id')->nullable();
$table->unique(['device_id', 'lldpLocPortId']);
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('ports_lldplocportid');
}
};
93 changes: 93 additions & 0 deletions includes/discovery/functions.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,70 @@
use LibreNMS\Util\Number;
use LibreNMS\Util\UserFuncHelper;

function discoverLldpLocPortId($device)
{
d_echo('Store lldpLocPortId');
$module_start = microtime(true);

$live_ports = [];
$port_stats = [];
$live_ports = snmpwalk_cache_oid($device, 'lldpLocPortId', $port_stats, 'LLDP-MIB');

$db_lldp_ports = [];

// Replaced dbFetchRows() with DB::table to avoid Ignition query-recorder type error when bindings are used
$db_lldp_ports = DB::table('ports_lldplocportid')
->where('device_id', $device['device_id'])
->get()
->map(fn($r) => (array)$r) // convert stdClass to associative array
->all();

$db_ports = [];
$db_ports = DB::table('ports')
->where('device_id', $device['device_id'])
->get()
->map(fn($r) => (array)$r) // convert stdClass → associative array
->all();

foreach ($live_ports as $ifIndex => $snmp_data) {
$db_lldp_index = array_search($snmp_data['lldpLocPortId'], array_column($db_lldp_ports, 'lldpLocPortId'));
$port_index = array_search($ifIndex, array_column($db_ports, 'ifIndex'));

if (is_int($port_index)) {
$port_id = $db_ports[$port_index]['port_id'];
} else {
$port_id = NULL;
}
$data['device_id'] = $device['device_id'];
$data['lldpLocPortId'] = $snmp_data['lldpLocPortId'];
$data['port_id'] = $port_id;

if (!is_int($db_lldp_index)) {
d_echo('Adding: ' . $snmp_data['lldpLocPortId']);
dbInsert($data, 'ports_lldplocportid');
} else {
if ($port_id != $db_lldp_ports[$db_lldp_index]['port_id']) {
d_echo('Updating: ' . $snmp_data['lldpLocPortId'] . ' port_id ' . $db_lldp_ports[$db_lldp_index]['port_id'] . ' to ' . $port_id);
dbUpdate($data, 'ports_lldplocportid', '`device_id` = ? and `lldpLocPortId` = ?', [$data['device_id'], $data['lldpLocPortId']]);
} else {
d_echo('No Change: ' . $snmp_data['lldpLocPortId']);
}
}
}
foreach($db_lldp_ports as $db_data) {
$db_lldp_ports_index = array_search($db_data['lldpLocPortId'], array_column($live_ports, 'lldpLocPortId'));
if (!is_int($db_lldp_ports_index)) {
d_echo('Deleting: ' . $db_data['lldpLocPortId']);
dbDelete('ports_lldplocportid', '`device_id` = ? and `lldpLocPortId` = ?', [$device['device_id'], $db_data['lldpLocPortId']]);
}
}
$module_time = microtime(true) - $module_start;
$module_time = substr($module_time, 0, 5);
d_echo($module_time . ' seconds');

//dd('debug stop');
}

/**
* @param string $hostname
* @param array $device
Expand Down Expand Up @@ -927,6 +991,35 @@ function find_device_id($name = '', $ip = '', $mac_address = '')
return 0;
}



/**
* Try to find a port by lldp port id
*
* @param string $lldpremportid matched against ports.lldpLocPortId
* @param int $device_id restrict search to ports on a specific device
* @return int
*/
function find_port_id_by_lldp_port_id($lldpremportid, $device_id)
{
if (!$device_id) {
return 0;
}
d_echo($lldpremportid . ':' . $device_id);
$sql = "SELECT `port_id` FROM `ports_lldplocportid` WHERE `device_id`=? AND `lldpLocPortId`=? LIMIT 1";
$params[] = $device_id;
$params[] = $lldpremportid;

$remote_port_id = (int)dbFetchCell($sql, $params);
if (!$remote_port_id) {
return 0;
} else {
return $remote_port_id;
}
}



/**
* Try to find a port by ifDescr, ifName, ifAlias, or MAC
*
Expand Down