Refactor MAC utilities into a single class (#15379)

* Refactor MAC utils to a new utility class

* Apply fixes from StyleCI

* Inline functions
Add tests
Handle bridgeid format

* Apply fixes from StyleCI

* Dedicated code path for stp bridge parsing, and improve STP output a bit

* Correctly parse dot1dBaseBridgeAddress and don't store int in bool field

* trim any unexpected character from bridge addresses, add extra test data.

* better comment

* barsBridge can handle dot1dBaseBridgeAddress correctly now

* parseBridge, check for properly formatted mac first.

* update test data, empty data = empty mac

* Fix new usage after rebase

* import

---------

Co-authored-by: StyleCI Bot <[email protected]>
This commit is contained in:
Tony Murray
2023-10-06 07:34:14 -05:00
committed by GitHub
co-authored by GitHub StyleCI Bot
parent 908aef6596
commit d8c372bbf4
36 changed files with 340 additions and 264 deletions
+2 -25
View File
@@ -55,13 +55,13 @@ class Stp implements Module
{
$device = $os->getDevice();
echo 'Instances: ';
$instances = $os->discoverStpInstances();
echo 'Instances: ';
ModuleModelObserver::observe(\App\Models\Stp::class);
$this->syncModels($device, 'stpInstances', $instances);
echo "\nPorts: ";
$ports = $os->discoverStpPorts($instances);
echo "\nPorts: ";
ModuleModelObserver::observe(PortStp::class);
$this->syncModels($device, 'stpPorts', $ports);
@@ -109,27 +109,4 @@ class Stp implements Module
->get()->map->makeHidden(['port_stp_id', 'device_id', 'port_id']),
];
}
/**
* designated root is stored in format 2 octet bridge priority + MAC address, so we need to normalize it
*/
public function rootToMac(string $root): string
{
$dr = str_replace(['.', ' ', ':', '-'], '', strtolower($root));
return substr($dr, -12); //remove first two octets
}
public function designatedPort(string $dp): int
{
if (preg_match('/-(\d+)/', $dp, $matches)) {
// Syntax with "priority" dash "portID" like so : 32768-54, both in decimal
return (int) $matches[1];
}
// Port saved in format priority+port (ieee 802.1d-1998: clause 8.5.5.1)
$dp = substr($dp, -2); //discard the first octet (priority part)
return (int) hexdec($dp);
}
}
+8 -7
View File
@@ -41,8 +41,8 @@ use LibreNMS\Interfaces\Polling\Sensors\WirelessApCountPolling;
use LibreNMS\Interfaces\Polling\Sensors\WirelessClientsPolling;
use LibreNMS\Interfaces\Polling\Sensors\WirelessFrequencyPolling;
use LibreNMS\OS;
use LibreNMS\Util\Mac;
use LibreNMS\Util\Number;
use LibreNMS\Util\Rewrite;
class ArubaInstant extends OS implements
OSDiscovery,
@@ -77,10 +77,11 @@ class ArubaInstant extends OS implements
foreach ($ai_ap_data as $ai_ap => $ai_ap_oid) {
$value = $ai_ap_oid['aiAPCPUUtilization'];
$combined_oid = sprintf('%s::%s.%s', $ai_mib, 'aiAPCPUUtilization', Rewrite::oidMac($ai_ap));
$mac = Mac::parse($ai_ap);
$combined_oid = sprintf('%s::%s.%s', $ai_mib, 'aiAPCPUUtilization', $mac->oid());
$oid = snmp_translate($combined_oid, 'ALL', 'arubaos', '-On');
$description = $ai_ap_data[$ai_ap]['aiAPSerialNum'];
$processors[] = Processor::discover('aruba-instant', $this->getDeviceId(), $oid, Rewrite::macToHex($ai_ap), $description, 1, $value);
$processors[] = Processor::discover('aruba-instant', $this->getDeviceId(), $oid, $mac->hex(), $description, 1, $value);
} // end foreach
return $processors;
@@ -126,10 +127,10 @@ class ArubaInstant extends OS implements
// Clients Per Radio
foreach ($ap_data as $index => $entry) {
foreach ($entry['aiRadioClientNum'] as $radio => $value) {
$combined_oid = sprintf('%s::%s.%s.%s', $ai_mib, 'aiRadioClientNum', Rewrite::oidMac($index), $radio);
$combined_oid = sprintf('%s::%s.%s.%s', $ai_mib, 'aiRadioClientNum', Mac::parse($index)->oid(), $radio);
$oid = snmp_translate($combined_oid, 'ALL', 'arubaos', '-On');
$description = sprintf('%s Radio %s', $entry['aiAPSerialNum'], $radio);
$sensor_index = sprintf('%s.%s', Rewrite::macToHex($index), $radio);
$sensor_index = sprintf('%s.%s', Mac::parse($index)->hex(), $radio);
$sensors[] = new WirelessSensor('clients', $this->getDeviceId(), $oid, 'aruba-instant', $sensor_index, $description, $value);
}
}
@@ -250,10 +251,10 @@ class ArubaInstant extends OS implements
$value = $value * $multiplier;
}
$combined_oid = sprintf('%s::%s.%s.%s', $ai_mib, $mib, Rewrite::oidMac($ai_ap), $ai_ap_radio);
$combined_oid = sprintf('%s::%s.%s.%s', $ai_mib, $mib, Mac::parse($ai_ap)->oid(), $ai_ap_radio);
$oid = snmp_translate($combined_oid, 'ALL', 'arubaos', '-On');
$description = sprintf($desc, $ai_sg_data[$ai_ap]['aiAPSerialNum'], $ai_ap_radio);
$index = sprintf('%s.%s', Rewrite::macToHex($ai_ap), $ai_ap_radio);
$index = sprintf('%s.%s', Mac::parse($ai_ap)->hex(), $ai_ap_radio);
$sensors[] = new WirelessSensor($type, $this->getDeviceId(), $oid, 'aruba-instant', $index, $description, $value, $multiplier);
} // end foreach
+4 -4
View File
@@ -35,7 +35,7 @@ use LibreNMS\Interfaces\Discovery\Sensors\WirelessRateDiscovery;
use LibreNMS\Interfaces\Discovery\Sensors\WirelessRssiDiscovery;
use LibreNMS\Interfaces\Polling\Sensors\WirelessFrequencyPolling;
use LibreNMS\OS;
use LibreNMS\Util\Rewrite;
use LibreNMS\Util\Mac;
class Lcos extends OS implements
WirelessFrequencyDiscovery,
@@ -222,7 +222,7 @@ class Lcos extends OS implements
$sensors[$bssid] = new WirelessSensor(
'ccq',
$this->getDeviceId(),
'.1.3.6.1.4.1.2356.11.1.3.44.1.10.' . Rewrite::oidMac($bssid) . '.0',
'.1.3.6.1.4.1.2356.11.1.3.44.1.10.' . Mac::parse($bssid)->oid() . '.0',
'lcos',
$bssid,
'CCQ ' . $entry['lcsStatusWlanCompetingNetworksEntryInterpointPeerName'] . " $bssid",
@@ -255,7 +255,7 @@ class Lcos extends OS implements
$sensors[$bssid] = new WirelessSensor(
'rate',
$this->getDeviceId(),
'.1.3.6.1.4.1.2356.11.1.3.44.1.35.' . Rewrite::oidMac($bssid) . '.0',
'.1.3.6.1.4.1.2356.11.1.3.44.1.35.' . Mac::parse($bssid)->oid() . '.0',
'lcos-tx',
$bssid,
'TX Rate ' . $entry['lcsStatusWlanCompetingNetworksEntryInterpointPeerName'] . " $bssid",
@@ -290,7 +290,7 @@ class Lcos extends OS implements
$sensors[$bssid] = new WirelessSensor(
'rssi',
$this->getDeviceId(),
'.1.3.6.1.4.1.2356.11.1.3.44.1.26.' . Rewrite::oidMac($bssid) . '.0',
'.1.3.6.1.4.1.2356.11.1.3.44.1.26.' . Mac::parse($bssid)->oid() . '.0',
'lcos',
$bssid,
'RSSI ' . $entry['lcsStatusWlanCompetingNetworksEntryInterpointPeerName'] . " $bssid",
+14 -12
View File
@@ -28,7 +28,7 @@ namespace LibreNMS\OS\Traits;
use App\Models\PortStp;
use App\Models\Stp;
use Illuminate\Support\Collection;
use LibreNMS\Util\Rewrite;
use LibreNMS\Util\Mac;
use SnmpQuery;
trait BridgeMib
@@ -67,19 +67,20 @@ trait BridgeMib
return new Collection;
}
$bridge = Rewrite::macToHex($stp['BRIDGE-MIB::dot1dBaseBridgeAddress.0'] ?? '');
$drBridge = Rewrite::macToHex($stp['BRIDGE-MIB::dot1dStpDesignatedRoot.0'] ?? '');
\Log::debug('VLAN: ' . ($vlan ?: 1) . " Bridge: {$bridge} DR: {$drBridge}");
$bridge = Mac::parseBridge($stp['BRIDGE-MIB::dot1dBaseBridgeAddress.0'] ?? '');
$bridgeMac = $bridge->hex();
$drBridge = Mac::parseBridge($stp['BRIDGE-MIB::dot1dStpDesignatedRoot.0'] ?? '');
\Log::info(sprintf('VLAN: %s Bridge: %s DR: %s', $vlan ?: 1, $bridge->readable(), $drBridge->readable()));
$instance = new \App\Models\Stp([
'vlan' => $vlan,
'rootBridge' => $bridge == $drBridge ? 1 : 0,
'bridgeAddress' => $bridge,
'rootBridge' => $bridgeMac == $drBridge->hex() ? 1 : 0,
'bridgeAddress' => $bridgeMac,
'protocolSpecification' => $stp['BRIDGE-MIB::dot1dStpProtocolSpecification.0'] ?? 'unknown',
'priority' => $stp['BRIDGE-MIB::dot1dStpPriority.0'] ?? 0,
'timeSinceTopologyChange' => substr($stp['BRIDGE-MIB::dot1dStpTimeSinceTopologyChange.0'] ?? '', 0, -2) ?: 0,
'topChanges' => $stp['BRIDGE-MIB::dot1dStpTopChanges.0'] ?? 0,
'designatedRoot' => $drBridge,
'designatedRoot' => $drBridge->hex(),
'rootCost' => $stp['BRIDGE-MIB::dot1dStpRootCost.0'] ?? 0,
'rootPort' => $stp['BRIDGE-MIB::dot1dStpRootPort.0'] ?? 0,
'maxAge' => ($stp['BRIDGE-MIB::dot1dStpMaxAge.0'] ?? 0) * $timeFactor,
@@ -109,9 +110,9 @@ trait BridgeMib
'state' => $data['BRIDGE-MIB::dot1dStpPortState'] ?? 'unknown',
'enable' => $data['BRIDGE-MIB::dot1dStpPortEnable'] ?? 'unknown',
'pathCost' => $data['BRIDGE-MIB::dot1dStpPortPathCost32'] ?? $data['BRIDGE-MIB::dot1dStpPortPathCost'] ?? 0,
'designatedRoot' => Rewrite::macToHex($data['BRIDGE-MIB::dot1dStpPortDesignatedRoot'] ?? ''),
'designatedRoot' => Mac::parseBridge($data['BRIDGE-MIB::dot1dStpPortDesignatedRoot'] ?? '')->hex(),
'designatedCost' => $data['BRIDGE-MIB::dot1dStpPortDesignatedCost'] ?? 0,
'designatedBridge' => Rewrite::macToHex($data['BRIDGE-MIB::dot1dStpPortDesignatedBridge'] ?? ''),
'designatedBridge' => Mac::parseBridge($data['BRIDGE-MIB::dot1dStpPortDesignatedBridge'] ?? '')->hex(),
'designatedPort' => $this->designatedPort($data['BRIDGE-MIB::dot1dStpPortDesignatedPort'] ?? ''),
'forwardTransitions' => $data['BRIDGE-MIB::dot1dStpPortForwardTransitions'] ?? 0,
]);
@@ -156,7 +157,8 @@ trait BridgeMib
$instance->timeSinceTopologyChange = substr($data['BRIDGE-MIB::dot1dStpTimeSinceTopologyChange.0'] ?? '', 0, -2) ?: 0;
$instance->topChanges = $data['BRIDGE-MIB::dot1dStpTopChanges.0'] ?? 0;
$instance->designatedRoot = Rewrite::macToHex($data['BRIDGE-MIB::dot1dStpDesignatedRoot.0'] ?? '');
$instance->designatedRoot = Mac::parseBridge($data['BRIDGE-MIB::dot1dStpDesignatedRoot.0'] ?? '')->hex();
$instance->rootBridge = $instance->bridgeAddress == $instance->designatedRoot; // dr might have changed
});
}
@@ -179,8 +181,8 @@ trait BridgeMib
$port->vlan = $vlan;
$port->state = $data['BRIDGE-MIB::dot1dStpPortState'] ?? 'unknown';
$port->enable = $data['BRIDGE-MIB::dot1dStpPortEnable'] ?? 'unknown';
$port->designatedRoot = Rewrite::macToHex($data['BRIDGE-MIB::dot1dStpPortDesignatedRoot'] ?? '');
$port->designatedBridge = Rewrite::macToHex($data['BRIDGE-MIB::dot1dStpPortDesignatedBridge'] ?? '');
$port->designatedRoot = Mac::parseBridge($data['BRIDGE-MIB::dot1dStpPortDesignatedRoot'] ?? '')->hex();
$port->designatedBridge = Mac::parseBridge($data['BRIDGE-MIB::dot1dStpPortDesignatedBridge'] ?? '')->hex();
return $port;
});
+164
View File
@@ -0,0 +1,164 @@
<?php
/**
* Mac.php
*
* -Description-
*
* 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 <https://www.gnu.org/licenses/>.
*
* @link https://www.librenms.org
*
* @copyright 2023 Tony Murray
* @author Tony Murray <[email protected]>
*/
namespace LibreNMS\Util;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
class Mac
{
private array $mac = [];
public function __construct(string $mac)
{
$mac = strtolower(trim($mac));
if (preg_match('/^([0-9a-f]{1,2})[-:.]?([0-9a-f]{1,2})[-:.]?([0-9a-f]{1,2})[-:.]?([0-9a-f]{1,2})[-:.]?([0-9a-f]{1,2})[-:.]?([0-9a-f]{1,2})$/', $mac, $matches)) {
// strings without delimiters must have 12 characters
if (! preg_match('/^[0-9a-f]{0,11}$/', $mac)) {
$this->mac = [
str_pad($matches[1], 2, '0', STR_PAD_LEFT),
str_pad($matches[2], 2, '0', STR_PAD_LEFT),
str_pad($matches[3], 2, '0', STR_PAD_LEFT),
str_pad($matches[4], 2, '0', STR_PAD_LEFT),
str_pad($matches[5], 2, '0', STR_PAD_LEFT),
str_pad($matches[6], 2, '0', STR_PAD_LEFT),
];
}
}
}
/**
* Parse a MAC address from a well-formed MAC string and in a common format.
* 00:12:34:ab:cd:ef
* 00:12:34:AB:CD:EF
* 0:12:34:AB:CD:EF
* 00-12-34-AB-CD-EF
* 001234-ABCDEF
* 0012.34AB.CDEF
* 00:02:04:0B:0D:0F
* 0:2:4:B:D:F
*/
public static function parse(?string $mac): static
{
return new static($mac ?? '');
}
/**
* Remove prefix from STP bridge addresses to parse MAC
* Examples: 80 00 3C 2C 99 7A 5D 80
* 0-1C.2C.99.7A.5D.80
*/
public static function parseBridge(string $bridge): static
{
$plainMac = new static($bridge);
if ($plainMac->isValid()) {
return $plainMac;
}
return new static(substr(preg_replace('/[^0-9a-f]/', '', strtolower($bridge)), -12));
}
/**
* Check if the parsed string was a valid MAC address
*/
public function isValid(): bool
{
return ! empty($this->mac);
}
/**
* Reformat the MAC to 12 digit hex string 000a1fa3cc14
*/
public function hex(): string
{
return implode($this->mac);
}
/**
* Reformat the MAC to a nice readable format 00:0a:1f:a3:cc:14
*/
public function readable()
{
return implode(':', $this->mac);
}
/**
* Extract the OUI and return the vendor's name
*/
public function vendor(): string
{
$oui = implode(array_slice($this->mac, 0, 3));
$results = Cache::remember($oui, 21600, function () use ($oui) {
return DB::table('vendor_ouis')
->where('oui', 'like', "$oui%") // possible matches
->orderBy('oui', 'desc') // so we can check longer ones first if we have them
->pluck('vendor', 'oui');
});
if (count($results) == 1) {
return Arr::first($results);
}
// Then we may have a shorter prefix, so let's try them one after the other
$mac = $this->hex();
foreach ($results as $oui => $vendor) {
if (str_starts_with($mac, $oui)) {
return $vendor;
}
}
return '';
}
/**
* Reformat hex MAC as oid MAC (dotted-decimal)
*
* 00:12:34:AB:CD:EF becomes 0.18.52.171.205.239
* 0:12:34:AB:CD:EF becomes 0.18.52.171.205.239
* 00:02:04:0B:0D:0F becomes 0.2.4.11.13.239
* 0:2:4:B:D:F becomes 0.2.4.11.13.15
*/
public function oid(): string
{
return implode('.', array_map('hexdec', $this->mac));
}
/**
* Get an array of the MAC address bytes
*/
public function array(): array
{
return $this->mac;
}
public function __toString(): string
{
return $this->readable();
}
}
-88
View File
@@ -26,9 +26,6 @@
namespace LibreNMS\Util;
use App\Models\Device;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use LibreNMS\Config;
class Rewrite
@@ -138,91 +135,6 @@ class Rewrite
return str_ireplace(array_keys($rewrite_shortif), array_values($rewrite_shortif), $name);
}
/**
* Reformat a mac stored in the DB (only hex) to a nice readable format
*
* @param string $mac
* @return string
*/
public static function readableMac($mac)
{
return rtrim(chunk_split($mac, 2, ':'), ':');
}
/**
* Extract the OUI and match it against database values
*
* @param string $mac
* @return string
*/
public static function readableOUI($mac): string
{
$oui = substr($mac, 0, 6);
$results = Cache::remember($oui, 21600, function () use ($oui) {
return DB::table('vendor_ouis')
->where('oui', 'like', "$oui%") // possible matches
->orderBy('oui', 'desc') // so we can check longer ones first if we have them
->pluck('vendor', 'oui');
});
if (count($results) == 1) {
return Arr::first($results);
}
// Then we may have a shorter prefix, so let's try them one after the other
foreach ($results as $oui => $vendor) {
if (str_starts_with($mac, $oui)) {
return $vendor;
}
}
return '';
}
/**
* Reformat hex MAC as oid MAC (dotted-decimal)
*
* 00:12:34:AB:CD:EF becomes 0.18.52.171.205.239
* 0:12:34:AB:CD:EF becomes 0.18.52.171.205.239
* 00:02:04:0B:0D:0F becomes 0.2.4.11.13.239
* 0:2:4:B:D:F becomes 0.2.4.11.13.15
*
* @param string $mac
* @return string oid representation of a MAC address
*/
public static function oidMac($mac)
{
return implode('.', array_map('hexdec', explode(':', $mac)));
}
/**
* Reformat Hex MAC with delimiters to Hex String without delimiters
*
* Assumes the MAC address is well-formed and in a common format.
* 00:12:34:ab:cd:ef becomes 001234abcdef
* 00:12:34:AB:CD:EF becomes 001234abcdef
* 0:12:34:AB:CD:EF becomes 001234abcdef
* 00-12-34-AB-CD-EF becomes 001234abcdef
* 001234-ABCDEF becomes 001234abcdef
* 0012.34AB.CDEF becomes 001234abcdef
* 00:02:04:0B:0D:0F becomes 0002040b0d0f
* 0:2:4:B:D:F becomes 0002040b0d0f
*
* @param string $mac hexadecimal MAC address with or without common delimiters
* @return string undelimited hexadecimal MAC address
*/
public static function macToHex(string $mac): string
{
// split it apart
$mac_array = explode(':', str_replace(['-', '.', ' '], ':', strtolower(trim($mac))));
$len = count($mac_array);
$mac_padding = array_fill(0, $len, ceil(12 / $len));
// pad the parts to prefix 0s and only take the last 12 digits
return substr(implode(array_map('zeropad', $mac_array, $mac_padding)), -12);
}
/**
* Make Cisco hardware human readable
*
@@ -34,7 +34,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use LibreNMS\Util\IP;
use LibreNMS\Util\Rewrite;
use LibreNMS\Util\Mac;
use LibreNMS\Util\Url;
class FdbTablesController extends TableController
@@ -159,10 +159,11 @@ class FdbTablesController extends TableController
*/
public function formatItem($fdb_entry)
{
$mac = Mac::parse($fdb_entry->mac_address);
$item = [
'device' => $fdb_entry->device ? Url::deviceLink($fdb_entry->device) : '',
'mac_address' => Rewrite::readableMac($fdb_entry->mac_address),
'mac_oui' => Rewrite::readableOUI($fdb_entry->mac_address),
'mac_address' => $mac->readable(),
'mac_oui' => $mac->vendor(),
'ipv4_address' => $fdb_entry->ipv4Addresses->implode(', '),
'interface' => '',
'vlan' => $fdb_entry->vlan ? $fdb_entry->vlan->vlan_vlan : '',
@@ -26,7 +26,7 @@
namespace App\Http\Controllers\Table;
use App\Models\PortsNac;
use LibreNMS\Util\Rewrite;
use LibreNMS\Util\Mac;
use LibreNMS\Util\Url;
class PortNacController extends TableController
@@ -86,9 +86,10 @@ class PortNacController extends TableController
public function formatItem($nac)
{
$item = $nac->toArray();
$mac = Mac::parse($item['mac_address']);
$item['port_id'] = Url::portLink($nac->port, $nac->port->getShortLabel());
$item['mac_oui'] = Rewrite::readableOUI($item['mac_address']);
$item['mac_address'] = Rewrite::readableMac($item['mac_address']);
$item['mac_oui'] = $mac->vendor();
$item['mac_address'] = $mac->readable();
$item['port'] = null; //free some unused data to be sent to the browser
$item['device_id'] = Url::deviceLink($nac->device);
@@ -28,7 +28,7 @@ namespace App\Http\Controllers\Table;
use App\Facades\DeviceCache;
use App\Models\PortStp;
use App\Models\Stp;
use LibreNMS\Util\Rewrite;
use LibreNMS\Util\Mac;
use LibreNMS\Util\Url;
class PortStpController extends TableController
@@ -85,6 +85,9 @@ class PortStpController extends TableController
*/
public function formatItem($stpPort)
{
$drMac = Mac::parse($stpPort->designatedRoot);
$dbMac = Mac::parse($stpPort->designatedBridge);
return [
'port_id' => Url::portLink($stpPort->port, $stpPort->port->getShortLabel()) . '<br />' . $stpPort->port->getDescription(),
'vlan' => $stpPort->vlan ?: 1,
@@ -92,12 +95,12 @@ class PortStpController extends TableController
'state' => $stpPort->state,
'enable' => $stpPort->enable,
'pathCost' => $stpPort->pathCost,
'designatedRoot' => Rewrite::readableMac($stpPort->designatedRoot),
'designatedRoot_vendor' => Rewrite::readableOUI($stpPort->designatedRoot),
'designatedRoot' => $drMac->readable(),
'designatedRoot_vendor' => $drMac->vendor(),
'designatedRoot_device' => Url::deviceLink(DeviceCache::get(Stp::where('bridgeAddress', $stpPort->designatedRoot)->value('device_id'))),
'designatedCost' => $stpPort->designatedCost,
'designatedBridge' => Rewrite::readableMac($stpPort->designatedBridge),
'designatedBridge_vendor' => Rewrite::readableOUI($stpPort->designatedBridge),
'designatedBridge' => $dbMac->readable(),
'designatedBridge_vendor' => $dbMac->vendor(),
'designatedBridge_device' => Url::deviceLink(DeviceCache::get(Stp::where('bridgeAddress', $stpPort->designatedBridge)->value('device_id'))),
'designatedPort' => $stpPort->designatedPort,
'forwardTransitions' => $stpPort->forwardTransitions,
+2 -1
View File
@@ -24,6 +24,7 @@
*/
use LibreNMS\Config;
use LibreNMS\Util\Mac;
foreach (DeviceCache::getPrimary()->getVrfContexts() as $context_name) {
if (file_exists(Config::get('install_dir') . "/includes/discovery/arp-table/{$device['os']}.inc.php")) {
@@ -69,7 +70,7 @@ foreach (DeviceCache::getPrimary()->getVrfContexts() as $context_name) {
$old_mac = $existing_data[$index]['mac_address'];
if ($mac != $old_mac && $mac != '') {
d_echo("Changed mac address for $ip from $old_mac to $mac\n");
log_event("MAC change: $ip : " . \LibreNMS\Util\Rewrite::readableMac($old_mac) . ' -> ' . \LibreNMS\Util\Rewrite::readableMac($mac), $device, 'interface', 4, $port_id);
log_event("MAC change: $ip : " . Mac::parse($old_mac)->readable() . ' -> ' . Mac::parse($mac)->readable(), $device, 'interface', 4, $port_id);
dbUpdate(['mac_address' => $mac], 'ipv4_mac', 'port_id=? AND ipv4_address=? AND context_name=?', [$port_id, $ip, $context_name]);
}
d_echo("$raw_mac => $ip\n", '.');
+14 -21
View File
@@ -42,8 +42,8 @@ use LibreNMS\Exceptions\InvalidTableColumnException;
use LibreNMS\Util\Graph;
use LibreNMS\Util\IP;
use LibreNMS\Util\IPv4;
use LibreNMS\Util\Mac;
use LibreNMS\Util\Number;
use LibreNMS\Util\Rewrite;
function api_success($result, $result_name, $message = null, $code = 200, $count = null, $extra = null): JsonResponse
{
@@ -2460,30 +2460,23 @@ function list_fdb(Illuminate\Http\Request $request)
function list_fdb_detail(Illuminate\Http\Request $request)
{
$macAddress = Rewrite::macToHex((string) $request->route('mac'));
$macAddress = Mac::parse($request->route('mac'));
$rules = [
'macAddress' => 'required|string|regex:/^[0-9a-fA-F]{12}$/',
];
$validate = Validator::make(['macAddress' => $macAddress], $rules);
if ($validate->fails()) {
return api_error(422, $validate->messages());
if (! $macAddress->isValid()) {
return api_error(422, 'Invalid MAC address');
}
$extras = ['mac' => Rewrite::readableMac($macAddress), 'mac_oui' => Rewrite::readableOUI($macAddress)];
$extras = ['mac' => $macAddress->readable(), 'mac_oui' => $macAddress->vendor()];
$fdb = PortsFdb::hasAccess(Auth::user())
->when(! empty($macAddress), function (Builder $query) use ($macAddress) {
return $query->leftJoin('ports', 'ports_fdb.port_id', 'ports.port_id')
->leftJoin('devices', 'ports_fdb.device_id', 'devices.device_id')
->where('mac_address', $macAddress)
->orderBy('ports_fdb.updated_at', 'desc')
->select('devices.hostname', 'ports.ifName', 'ports_fdb.updated_at');
})
->limit(1000)->get();
->leftJoin('ports', 'ports_fdb.port_id', 'ports.port_id')
->leftJoin('devices', 'ports_fdb.device_id', 'devices.device_id')
->where('mac_address', $macAddress->hex())
->orderBy('ports_fdb.updated_at', 'desc')
->select('devices.hostname', 'ports.ifName', 'ports_fdb.updated_at')
->limit(1000)->get();
if (count($fdb) == 0) {
if ($fdb->isEmpty()) {
return api_error(404, 'Fdb entry does not exist');
}
@@ -2558,7 +2551,7 @@ function list_arp(Illuminate\Http\Request $request)
return api_error(400, 'Invalid Network Address');
}
} elseif (filter_var($query, FILTER_VALIDATE_MAC)) {
$mac = \LibreNMS\Util\Rewrite::macToHex($query);
$mac = Mac::parse($query)->hex();
$arp = dbFetchRows('SELECT * FROM `ipv4_mac` WHERE `mac_address`=?', [$mac]);
} else {
$arp = dbFetchRows('SELECT * FROM `ipv4_mac` WHERE `ipv4_address`=?', [$query]);
@@ -2967,7 +2960,7 @@ function del_service_from_host(Illuminate\Http\Request $request)
function search_by_mac(Illuminate\Http\Request $request)
{
$macAddress = Rewrite::macToHex((string) $request->route('search'));
$macAddress = Mac::parse((string) $request->route('search'))->hex();
$rules = [
'macAddress' => 'required|string|regex:/^[0-9a-fA-F]{12}$/',
@@ -1,6 +1,7 @@
<?php
use LibreNMS\Exceptions\RrdGraphException;
use LibreNMS\Util\Mac;
if (is_numeric($vars['id'])) {
$acc = dbFetchRow('SELECT * FROM `mac_accounting` AS M, `ports` AS I, `devices` AS D WHERE M.ma_id = ? AND I.port_id = M.port_id AND I.device_id = D.device_id', [$vars['id']]);
@@ -24,7 +25,7 @@ if (is_numeric($vars['id'])) {
$device = device_by_id_cache($port['device_id']);
$title = generate_device_link($device);
$title .= ' :: Port ' . generate_port_link($port);
$title .= ' :: ' . \LibreNMS\Util\Rewrite::readableMac($acc['mac']);
$title .= ' :: ' . Mac::parse($acc['mac'])->readable();
$auth = true;
} else {
throw new RrdGraphException('file not found');
@@ -1,5 +1,7 @@
<?php
use LibreNMS\Util\Mac;
$port = $vars['id'];
$stat = $vars['stat'] ?: 'bits';
$sort = in_array($vars['sort'], ['in', 'out', 'both']) ? $vars['sort'] : 'in';
@@ -49,7 +51,7 @@ $rrd_options .= " COMMENT:' In\: Current
foreach ($accs as $acc) {
$this_rrd = Rrd::name($acc['hostname'], ['cip', $acc['ifIndex'], $acc['mac']]);
if (Rrd::checkRrdExists($this_rrd)) {
$mac = \LibreNMS\Util\Rewrite::readableMac($acc['mac']);
$mac = Mac::parse($acc['mac'])->readable();
$name = $mac;
$addy = dbFetchRow('SELECT * FROM ipv4_mac where mac_address = ? AND port_id = ?', [$acc['mac'], $acc['port_id']]);
@@ -1,6 +1,7 @@
<?php
use LibreNMS\Config;
use LibreNMS\Util\Mac;
// FIXME - REWRITE!
$hostname = $device['hostname'];
@@ -186,7 +187,7 @@ if ($vars['subview'] == 'top10') {
echo '
<table>
<tr>
<td class=list-large width=200>' . \LibreNMS\Util\Rewrite::readableMac($acc['mac']) . '</td>
<td class=list-large width=200>' . Mac::parse($acc['mac'])->readable() . '</td>
<td class=list-large width=200>' . $addy['ipv4_address'] . '</td>
<td class=list-large width=500>' . $name . ' ' . $arp_name . '</td>
<td class=list-large width=100>' . \LibreNMS\Util\Number::formatSi($acc['cipMacHCSwitchedBytes_input_rate'] / 8, 2, 3, 'bps') . '</td>
+4 -2
View File
@@ -1,6 +1,7 @@
<?php
use LibreNMS\Util\IP;
use LibreNMS\Util\Mac;
$param = [];
@@ -87,8 +88,9 @@ foreach (dbFetchRows($sql, $param) as $interface) {
if ($vars['search_type'] == 'ipv6') {
$address = (string) IP::parse($interface['ipv6_address'], true) . '/' . $interface['ipv6_prefixlen'];
} elseif ($vars['search_type'] == 'mac') {
$address = \LibreNMS\Util\Rewrite::readableMac($interface['ifPhysAddress']);
$mac_oui = \LibreNMS\Util\Rewrite::readableOUI($interface['ifPhysAddress']);
$mac = Mac::parse($interface['ifPhysAddress']);
$address = $mac->readable();
$mac_oui = $mac->vendor();
} else {
$address = (string) IP::parse($interface['ipv4_address'], true) . '/' . $interface['ipv4_prefixlen'];
}
+5 -2
View File
@@ -1,5 +1,7 @@
<?php
use LibreNMS\Util\Mac;
$param = [];
$sql = ' FROM `ipv4_mac` AS M, `ports` AS P, `devices` AS D ';
@@ -95,9 +97,10 @@ foreach (dbFetchRows($sql, $param) as $entry) {
$arp_if = 'Local port';
}
$mac = Mac::parse($entry['mac_address']);
$response[] = [
'mac_address' => \LibreNMS\Util\Rewrite::readableMac($entry['mac_address']),
'mac_oui' => \LibreNMS\Util\Rewrite::readableOUI($entry['mac_address']),
'mac_address' => $mac->readable(),
'mac_oui' => $mac->vendor(),
'ipv4_address' => $entry['ipv4_address'],
'hostname' => generate_device_link($entry),
'interface' => generate_port_link($entry, makeshortif($entry['label'])) . ' ' . $error_img,
@@ -1,6 +1,7 @@
<?php
use LibreNMS\RRD\RrdDefinition;
use LibreNMS\Util\Mac;
if ($device['os_group'] == 'cisco') {
$acc_rows = dbFetchRows('SELECT *, A.poll_time AS poll_time FROM `mac_accounting` as A, `ports` AS I where A.port_id = I.port_id AND I.device_id = ?', [$device['device_id']]);
@@ -27,7 +28,7 @@ if ($device['os_group'] == 'cisco') {
foreach ($cip_response->table(3) as $ifIndex => $port_data) {
foreach ($port_data as $direction => $dir_data) {
foreach ($dir_data as $mac => $mac_data) {
$mac = \LibreNMS\Util\Rewrite::macToHex($mac);
$mac = Mac::parse($mac)->hex();
$cip_array[$ifIndex][$mac]['cipMacHCSwitchedBytes'][$direction] = $mac_data['CISCO-IP-STAT-MIB::cipMacHCSwitchedBytes'] ?? $mac_data['CISCO-IP-STAT-MIB::cipMacSwitchedBytes'] ?? null;
$cip_array[$ifIndex][$mac]['cipMacHCSwitchedPkts'][$direction] = $mac_data['CISCO-IP-STAT-MIB::cipMacHCSwitchedPkts'] ?? $mac_data['CISCO-IP-STAT-MIB::cipMacSwitchedPkts'] ?? null;
}
+8 -9
View File
@@ -14,10 +14,10 @@
<tr>
<td>{{ trans('stp.bridge_address') }}</td>
<td>
{{ \LibreNMS\Util\Rewrite::readableMac($instance['bridgeAddress']) }}
{{ \LibreNMS\Util\Mac::parse($instance['bridgeAddress'])->readable() }}
@if($url = \LibreNMS\Util\Url::deviceLink(\App\Facades\DeviceCache::get(\App\Models\Stp::where('bridgeAddress', $instance['bridgeAddress'])->value('device_id'))))
({!! $url !!})
@elseif($brVendor = \LibreNMS\Util\Rewrite::readableOUI($instance['bridgeAddress']))
({!! $url !!})
@elseif($brVendor = \LibreNMS\Util\Mac::parse($instance['bridgeAddress'])->vendor())
({{ $brVendor }})
@endif
</td>
@@ -41,10 +41,10 @@
<tr>
<td>{{ trans('stp.designated_root') }}</td>
<td>
{{ \LibreNMS\Util\Rewrite::readableMac($instance['designatedRoot']) }}
{{ Mac::parse($instance['designatedRoot'])->readable() }}
@if($url = \LibreNMS\Util\Url::deviceLink(\App\Facades\DeviceCache::get(\App\Models\Stp::where('bridgeAddress', $instance['designatedRoot'])->value('device_id'))))
({!! $url !!})
@elseif($drVendor = \LibreNMS\Util\Rewrite::readableOUI($instance['designatedRoot']))
({!! $url !!})
@elseif($drVendor = \LibreNMS\Util\Mac::parse($instance['designatedRoot'])->vendor())
({{ $drVendor }})
@endif
</td>
@@ -117,7 +117,7 @@
@push('scripts')
<script>
var grid = $("#stp-ports").bootgrid( {
var grid = $("#stp-ports").bootgrid({
ajax: true,
templates: {search: ""},
formatters: {
@@ -130,8 +130,7 @@
return html;
}
},
post: function ()
{
post: function () {
return {
device_id: '{{ $data['device_id'] }}',
vlan: '{{ $data['vlan'] }}',
-6
View File
@@ -27,16 +27,10 @@ namespace LibreNMS\Tests;
use LibreNMS\Device\YamlDiscovery;
use LibreNMS\Util\Number;
use LibreNMS\Util\Rewrite;
use LibreNMS\Util\Time;
class FunctionsTest extends TestCase
{
public function testMacCleanToReadable(): void
{
$this->assertEquals('de:ad:be:ef:a0:c3', Rewrite::readableMac('deadbeefa0c3'));
}
public function testHex2Str(): void
{
$this->assertEquals('Big 10 UP', hex2str('426967203130205550'));
+2 -1
View File
@@ -33,6 +33,7 @@ use Illuminate\Support\Str;
use LibreNMS\Data\Source\NetSnmpQuery;
use LibreNMS\Data\Source\SnmpQueryInterface;
use LibreNMS\Data\Source\SnmpResponse;
use LibreNMS\Util\Mac;
use LibreNMS\Util\Oid;
use Log;
@@ -243,7 +244,7 @@ class SnmpQueryMock implements SnmpQueryInterface
'1.3.6.1.2.1.17.1.1.0', // BRIDGE-MIB::dot1dBaseBridgeAddress.0
'1.3.6.1.4.1.890.1.5.13.13.8.1.1.20', // IES5206-MIB::slotModuleMacAddress
])) {
$data = \LibreNMS\Util\Rewrite::readableMac($data);
$data = Mac::parse($data)->readable();
} else {
$data = hex2str($data);
}
+57
View File
@@ -0,0 +1,57 @@
<?php
namespace LibreNMS\Tests\Unit\Util;
use LibreNMS\Tests\TestCase;
use LibreNMS\Util\Mac;
class MacUtilTest extends TestCase
{
public function testMacOutput(): void
{
$mac = Mac::parse('DeadBeefa0c3');
$this->assertTrue($mac->isValid());
$this->assertEquals('de:ad:be:ef:a0:c3', $mac->readable());
$this->assertEquals('deadbeefa0c3', $mac->hex());
$this->assertEquals('222.173.190.239.160.195', $mac->oid());
$this->assertEquals(['de', 'ad', 'be', 'ef', 'a0', 'c3'], $mac->array());
}
public function testBridgeParsing(): void
{
$this->assertEquals('0c85255ce500', Mac::parseBridge('80 62 0c 85 25 5c e5 00')->hex());
$this->assertEquals('000000000001', Mac::parseBridge('00 00 00 00 00 00 00 01 ')->hex());
$this->assertEquals('000000000002', Mac::parseBridge('0-00.00.00.00.00.02')->hex());
$this->assertEquals('00186e6449a0', Mac::parseBridge('0:18:6e:64:49:a0')->hex());
$this->assertEquals('0c85255ce500', Mac::parseBridge('80620c85255ce500')->hex());
}
/**
* @test
*
* @dataProvider validMacProvider
*/
public function testMacToHex(string $from, string $to): void
{
$this->assertEquals($to, Mac::parse($from)->hex());
}
public function validMacProvider(): array
{
return [
['00:00:00:00:00:01', '000000000001'],
['00-00-00-00-00-01', '000000000001'],
['000000.000001', '000000000001'],
['000000000001', '000000000001'],
['00:12:34:ab:cd:ef', '001234abcdef'],
['00:12:34:AB:CD:EF', '001234abcdef'],
['0:12:34:AB:CD:EF', '001234abcdef'],
['00-12-34-AB-CD-EF', '001234abcdef'],
['001234-ABCDEF', '001234abcdef'],
['0012.34AB.CDEF', '001234abcdef'],
['00:02:04:0B:0D:0F', '0002040b0d0f'],
['0:2:4:B:D:F', '0002040b0d0f'],
['0:2:4:B:D:F', '0002040b0d0f'],
];
}
}
-40
View File
@@ -1,40 +0,0 @@
<?php
namespace LibreNMS\Tests\Unit\Util;
use LibreNMS\Tests\TestCase;
use LibreNMS\Util\Rewrite;
class RewriteTest extends TestCase
{
/**
* @test
*
* @dataProvider validMacProvider
*/
public function testMacToHex(string $from, string $to): void
{
$this->assertEquals($to, Rewrite::macToHex($from));
}
public function validMacProvider(): array
{
return [
['00:00:00:00:00:01', '000000000001'],
['00-00-00-00-00-01', '000000000001'],
['000000.000001', '000000000001'],
['000000000001', '000000000001'],
['00:12:34:ab:cd:ef', '001234abcdef'],
['00:12:34:AB:CD:EF', '001234abcdef'],
['0:12:34:AB:CD:EF', '001234abcdef'],
['00-12-34-AB-CD-EF', '001234abcdef'],
['001234-ABCDEF', '001234abcdef'],
['0012.34AB.CDEF', '001234abcdef'],
['00:02:04:0B:0D:0F', '0002040b0d0f'],
['0:2:4:B:D:F', '0002040b0d0f'],
['0:2:4:B:D:F', '0002040b0d0f'],
['80 62 0c 85 25 5c e5 00', '0c85255ce500'], // BridgeId format
['80620c85255ce500', '0c85255ce500'],
];
}
}
+2 -2
View File
@@ -4855,8 +4855,8 @@
"stp": [
{
"vlan": null,
"rootBridge": 1,
"bridgeAddress": "000000000000",
"rootBridge": 0,
"bridgeAddress": "",
"protocolSpecification": "ieee8021d",
"priority": 0,
"timeSinceTopologyChange": "0",
@@ -19945,8 +19945,8 @@
"stp": [
{
"vlan": null,
"rootBridge": 1,
"bridgeAddress": "000000000000",
"rootBridge": 0,
"bridgeAddress": "",
"protocolSpecification": "ieee8021d",
"priority": 32768,
"timeSinceTopologyChange": "0",
@@ -20045,8 +20045,8 @@
"stp": [
{
"vlan": null,
"rootBridge": 1,
"bridgeAddress": "000000000000",
"rootBridge": 0,
"bridgeAddress": "",
"protocolSpecification": "ieee8021d",
"priority": 32768,
"timeSinceTopologyChange": "0",
+1 -1
View File
@@ -3746,7 +3746,7 @@
"priority": 0,
"timeSinceTopologyChange": "21474836",
"topChanges": 0,
"designatedRoot": "000000000000",
"designatedRoot": "",
"rootCost": 0,
"rootPort": 0,
"maxAge": 0,
+1 -1
View File
@@ -6412,7 +6412,7 @@
"priority": 65535,
"timeSinceTopologyChange": "0",
"topChanges": 0,
"designatedRoot": "000000000000",
"designatedRoot": "",
"rootCost": 0,
"rootPort": 0,
"maxAge": 0,
+2 -2
View File
@@ -3597,13 +3597,13 @@
"stp": [
{
"vlan": null,
"rootBridge": 1,
"rootBridge": 0,
"bridgeAddress": "000000000000",
"protocolSpecification": "ieee8021d",
"priority": 32768,
"timeSinceTopologyChange": "0",
"topChanges": 0,
"designatedRoot": "000000000000",
"designatedRoot": "",
"rootCost": 0,
"rootPort": 0,
"maxAge": 0,
+1 -1
View File
@@ -5773,7 +5773,7 @@
"priority": 28672,
"timeSinceTopologyChange": "0",
"topChanges": 0,
"designatedRoot": "000000000000",
"designatedRoot": "",
"rootCost": 0,
"rootPort": 0,
"maxAge": 0,
+6 -6
View File
@@ -1355,7 +1355,7 @@
"priority": 32768,
"timeSinceTopologyChange": "0",
"topChanges": 0,
"designatedRoot": "000000000000",
"designatedRoot": "",
"rootCost": 0,
"rootPort": 0,
"maxAge": 0,
@@ -1377,7 +1377,7 @@
"pathCost": 10,
"designatedRoot": "000000000000",
"designatedCost": 0,
"designatedBridge": "000000000000",
"designatedBridge": "",
"designatedPort": 0,
"forwardTransitions": 0,
"ifIndex": 1
@@ -1391,7 +1391,7 @@
"pathCost": 10,
"designatedRoot": "000000000000",
"designatedCost": 0,
"designatedBridge": "000000000000",
"designatedBridge": "",
"designatedPort": 0,
"forwardTransitions": 0,
"ifIndex": 2
@@ -1405,7 +1405,7 @@
"pathCost": 10,
"designatedRoot": "000000000000",
"designatedCost": 0,
"designatedBridge": "000000000000",
"designatedBridge": "",
"designatedPort": 0,
"forwardTransitions": 0,
"ifIndex": 3
@@ -1419,7 +1419,7 @@
"pathCost": 10,
"designatedRoot": "000000000000",
"designatedCost": 0,
"designatedBridge": "000000000000",
"designatedBridge": "",
"designatedPort": 0,
"forwardTransitions": 0,
"ifIndex": 4
@@ -1433,7 +1433,7 @@
"pathCost": 10,
"designatedRoot": "000000000000",
"designatedCost": 0,
"designatedBridge": "000000000000",
"designatedBridge": "",
"designatedPort": 0,
"forwardTransitions": 0,
"ifIndex": 5
+5 -5
View File
@@ -2761,7 +2761,7 @@
"priority": 32768,
"timeSinceTopologyChange": "0",
"topChanges": 0,
"designatedRoot": "000000000000",
"designatedRoot": "",
"rootCost": 0,
"rootPort": 0,
"maxAge": 0,
@@ -2783,7 +2783,7 @@
"pathCost": 10,
"designatedRoot": "000000000000",
"designatedCost": 0,
"designatedBridge": "000000000000",
"designatedBridge": "",
"designatedPort": 0,
"forwardTransitions": 0,
"ifIndex": 1
@@ -2797,7 +2797,7 @@
"pathCost": 10,
"designatedRoot": "c006c3a13f46",
"designatedCost": 20000,
"designatedBridge": "000000000000",
"designatedBridge": "",
"designatedPort": 9,
"forwardTransitions": 0,
"ifIndex": 6
@@ -2811,7 +2811,7 @@
"pathCost": 10,
"designatedRoot": "000000000000",
"designatedCost": 0,
"designatedBridge": "000000000000",
"designatedBridge": "",
"designatedPort": 0,
"forwardTransitions": 0,
"ifIndex": 5
@@ -2825,7 +2825,7 @@
"pathCost": 10,
"designatedRoot": "000000000000",
"designatedCost": 0,
"designatedBridge": "000000000000",
"designatedBridge": "",
"designatedPort": 0,
"forwardTransitions": 0,
"ifIndex": 4
+2 -2
View File
@@ -659,12 +659,12 @@
{
"vlan": null,
"rootBridge": 1,
"bridgeAddress": "000000000000",
"bridgeAddress": "",
"protocolSpecification": "ieee8021d",
"priority": 0,
"timeSinceTopologyChange": "0",
"topChanges": 0,
"designatedRoot": "000000000000",
"designatedRoot": "",
"rootCost": 0,
"rootPort": 0,
"maxAge": 0,
+3 -3
View File
@@ -1056,7 +1056,7 @@
"priority": 32768,
"timeSinceTopologyChange": "0",
"topChanges": 0,
"designatedRoot": "000000000000",
"designatedRoot": "",
"rootCost": 0,
"rootPort": 0,
"maxAge": 0,
@@ -1078,7 +1078,7 @@
"pathCost": 10,
"designatedRoot": "788a20fa53cb",
"designatedCost": 0,
"designatedBridge": "000000000000",
"designatedBridge": "",
"designatedPort": 10,
"forwardTransitions": 0,
"ifIndex": 2
@@ -1092,7 +1092,7 @@
"pathCost": 10,
"designatedRoot": "000000000000",
"designatedCost": 0,
"designatedBridge": "000000000000",
"designatedBridge": "",
"designatedPort": 0,
"forwardTransitions": 0,
"ifIndex": 4
+1 -1
View File
@@ -11967,7 +11967,7 @@
"priority": 65535,
"timeSinceTopologyChange": "0",
"topChanges": 0,
"designatedRoot": "000000000000",
"designatedRoot": "",
"rootCost": 0,
"rootPort": 0,
"maxAge": 0,
+1 -1
View File
@@ -2733,7 +2733,7 @@
{
"vlan": null,
"rootBridge": 0,
"bridgeAddress": "000000000000",
"bridgeAddress": "",
"protocolSpecification": "ieee8021d",
"priority": 24576,
"timeSinceTopologyChange": "0",
+2 -2
View File
@@ -51667,8 +51667,8 @@
"stp": [
{
"vlan": null,
"rootBridge": 1,
"bridgeAddress": "000000000000",
"rootBridge": 0,
"bridgeAddress": "",
"protocolSpecification": "ieee8021d",
"priority": 32768,
"timeSinceTopologyChange": "0",