$lldp) { if (str_contains_array($index, '.')) { // This is correct RFC case: // LLDP-MIB::lldpRemChassisIdSubtype.0.0.1 = INTEGER: macAddress(4) [$lldpRemTimeMark, $lldpRemLocalPortNum, $lldpRemIndex] = explode('.', $index); } else { // Incorrect case (ie on old RouterOS): // LLDP-MIB::lldpRemChassisIdSubtype.1495 = INTEGER: macAddress(4) $lldpRemTimeMark = 0; $lldpRemLocalPortNum = 0; $lldpRemIndex = $index; } // Detect local device port $port = NULL; // Prefer by LLDP-MIB if (!$port && !empty($lldp_local_array[$lldpRemLocalPortNum]['lldpLocPortDesc'])) { //lldpLocPortIdSubtype.15 = interfaceName //lldpLocPortIdSubtype.16 = interfaceName //lldpLocPortId.15 = "Te1/15" //lldpLocPortId.16 = "Te1/16" //lldpLocPortDesc.15 = TenGigabitEthernet1/15 //lldpLocPortDesc.16 = TenGigabitEthernet1/16 $ifName = $lldp_local_array[$lldpRemLocalPortNum]['lldpLocPortDesc']; if ($ifName === 'OOBM') { // procurve: // ifDescr.5481 = Out Of Band Management Port // ifDescr.5492 = Out Of Band Management loopback Interface // ifName.5481 = oobm0 // ifName.5492 = lo0 // lldpLocPortIdSubtype.4000 = local // lldpLocPortId.4000 = "4000" // lldpLocPortDesc.4000 = OOBM $ifName .= '0'; } $port = dbFetchRow("SELECT * FROM `ports` WHERE `device_id` = ? AND `deleted` = ? AND (`ifName`= ? OR `ifDescr` = ? OR `port_label_short` = ?)", [$device['device_id'], 0, $ifName, $ifName, $ifName]); } // By BRIDGE-MIB (Warning, seems as more hard on multiple platforms not correctly association with ifIndex for LLDP) if (!$port && is_numeric($dot1d_array[$lldpRemLocalPortNum]['dot1dBasePortIfIndex']) && !in_array($device['os'], [ 'junos', 'junos-evo', 'dell-os10' ])) { // Incorrect association on these platforms // Gets the port using BRIDGE-MIB $ifIndex = $dot1d_array[$lldpRemLocalPortNum]['dot1dBasePortIfIndex']; $port = dbFetchRow("SELECT * FROM `ports` WHERE `device_id` = ? AND `deleted` = ? AND `ifIndex` = ? AND `ifDescr` NOT LIKE 'Vlan%'", [$device['device_id'], 0, $ifIndex]); } // last try by lldpLocPortId, also see below for remote port if (!$port) { switch ($lldp_local_array[$lldpRemLocalPortNum]['lldpLocPortIdSubtype']) { case 'interfaceName': $ifName = snmp_hexstring($lldp_local_array[$lldpRemLocalPortNum]['lldpLocPortId']); $port = dbFetchRow("SELECT * FROM `ports` WHERE `device_id` = ? AND `deleted` = ? AND (`ifName` = ? OR `ifDescr` = ? OR `port_label_short` = ?)", [$device['device_id'], 0, $ifName, $ifName, $ifName]); break; case 'interfaceAlias': $ifAlias = snmp_hexstring($lldp_local_array[$lldpRemLocalPortNum]['lldpLocPortId']); $port = dbFetchRow("SELECT * FROM `ports` WHERE `device_id` = ? AND `deleted` = ? AND `ifAlias` = ?", [$device['device_id'], 0, $ifAlias]); break; case 'macAddress': $ifPhysAddress = strtolower(str_replace(' ', '', $lldp_local_array[$lldpRemLocalPortNum]['lldpLocPortId'])); $port = dbFetchRow("SELECT * FROM `ports` WHERE `device_id` = ? AND `deleted` = ? AND `ifPhysAddress` = ?", [$device['device_id'], 0, $ifPhysAddress]); break; case 'networkAddress': $ip = snmp_hexstring($lldp_local_array[$lldpRemLocalPortNum]['lldpLocPortId']); /* $ip_version = get_ip_version($id); if ($ip_version) { $ip = ($ip_version === 6 ? Net_IPv6::uncompress($id, TRUE) : $id); $port = dbFetchRow("SELECT * FROM `ipv".$ip_version."_addresses` LEFT JOIN `ports` USING (`port_id`) WHERE `ipv".$ip_version."_address` = ? AND `device_id` = ?", array($ip, $device['device_id'])); } unset($id, $ip); */ $peer_where = generate_query_values_and($device['device_id'], 'device_id'); // Additional filter for include self IPs // Fetch all devices with peer IP and filter by UP if ($ids = get_entity_ids_ip_by_network('port', $ip, $peer_where)) { $port = get_port_by_id_cache($ids[0]); } break; } } // Ohh still unknown port? this is not should happen, but this derp LLDP implementation on your device if (!$port && is_numeric($lldpRemLocalPortNum)) { print_debug('WARNING. Local port for neighbour not found, used incorrect lldpRemLocalPortNum as ifIndex.'); $ifIndex = $lldpRemLocalPortNum; // This is incorrect, not really ifIndex, but seems sometime this numbers same $port = dbFetchRow("SELECT * FROM `ports` WHERE `device_id` = ? AND `deleted` = ? AND `ifIndex` = ? AND `ifDescr` NOT LIKE 'Vlan%'", [$device['device_id'], 0, $ifIndex]); if (!$port && $device['os'] === 'ciena-waveserveros') { $ifName = 'PORT-' . snmp_hexstring($lldp_local_array[$lldpRemLocalPortNum]['lldpLocPortId']); $port = dbFetchRow("SELECT * FROM `ports` WHERE `device_id` = ? AND `deleted` = ? AND (`ifName` = ? OR `ifDescr` = ? OR `port_label_short` = ?)", [$device['device_id'], 0, $ifName, $ifName, $ifName]); } } $remote_device_id = FALSE; $remote_port_id = NULL; // Sometime lldpRemPortDesc is not set if (!isset($lldp['lldpRemPortDesc'])) { $lldp['lldpRemPortDesc'] = ''; } $remote_mac = $lldp['lldpRemChassisIdSubtype'] === 'macAddress' ? $lldp['lldpRemChassisId'] : NULL; // lldpRemPortId can be hex string if ($lldp['lldpRemPortIdSubtype'] !== 'macAddress') { // On Extreme platforms, they remove the leading 1: from ports. Put it back if there isn't a :. if (preg_match('/^ExtremeXOS.*$/', $lldp['lldpRemSysDesc'])) { if (!preg_match('/\:/', $lldp['lldpRemPortId'])) { $lldp['lldpRemPortId'] = '1:' . $lldp['lldpRemPortId']; } } else { //$lldp['lldpRemPortId'] = snmp_hexstring($lldp['lldpRemPortId']); } } elseif (safe_empty($remote_mac)) { $remote_mac = $lldp['lldpRemPortId']; } // Cumulus Linux have empty lldpRemSysDesc if (!isset($lldp['lldpRemSysDesc']) && isset($lldp['lldpXMedRemModelName'])) { $lldp['lldpRemSysDesc'] = trim($lldp['lldpXMedRemMfgName'] . ' ' . $lldp['lldpXMedRemModelName']); } $lldp['lldpRemSysVersion'] = safe_empty($lldp['lldpXMedRemSoftwareRev']) ? NULL : $lldp['lldpXMedRemSoftwareRev']; // Remote sysname is MAC in some cases // lldpRemChassisIdSubtype.0.16.1 = macAddress // lldpRemChassisId.0.16.1 = "40 E3 D6 CE 8A 9A " // lldpRemPortIdSubtype.0.16.1 = macAddress // lldpRemPortId.0.16.1 = "40 E3 D6 CE 8A 9A " // lldpRemSysName.0.16.1 = 40:e3:d6:ce:8a:9a // lldpRemManAddrIfId.0.16.1.1.4.10.10.14.132 = 8 if (preg_match('/^[a-f\d]{2}([: ][a-f\d]{2}){5}$/', $lldp['lldpRemSysName']) && get_ip_version($lldp['lldpRemManAddr'])) { // Replace by IP address for better discovery print_debug("LLDP hostname replaced: " . $lldp['lldpRemSysName'] . " -> " . $lldp['lldpRemManAddr']); $lldp['lldpRemSysName'] = $lldp['lldpRemManAddr']; } elseif (safe_empty($lldp['lldpRemSysName']) && !safe_empty($lldp['lldpRemSysDesc'])) { if ($lldp['lldpRemChassisIdSubtype'] === 'macAddress') { // use mac address instead empty hostname: // lldpRemChassisIdSubtype.89.3.1 = macAddress // lldpRemChassisId.89.3.1 = "6C B0 CE 12 A9 A8 " // lldpRemPortIdSubtype.89.3.1 = local // lldpRemPortId.89.3.1 = "g25" // lldpRemPortDesc.89.3.1 = g25 // lldpRemSysName.89.3.1 = // lldpRemSysDesc.89.3.1 = GS724Tv4 ProSafe 24-port Gigabit Ethernet Smart Switch, 6.3.1.4, B1.0.0.4 // lldpRemManAddrIfId.89.3.1.1.4.192.168.0.239 = 51 $lldp['lldpRemSysName'] = str_replace([' ', '-'], '', strtolower($lldp['lldpRemChassisId'])); } elseif ($lldp['lldpRemChassisIdSubtype'] === 'networkAddress' && preg_match('/^01 (?([A-F\d]{2}\s?){4})$/', $lldp['lldpRemChassisId'], $matches)) { $lldp['lldpRemSysName'] = hex2ip($matches['ip']); } } // Try find remote device and check if already cached $remote_device_id = get_autodiscovery_device_id($device, $lldp['lldpRemSysName'], $lldp['lldpRemManAddr'], $remote_mac); if (is_null($remote_device_id) && // NULL - never cached in other rounds check_autodiscovery($lldp['lldpRemSysName'], $lldp['lldpRemManAddr'])) { // Check all previous autodiscovery rounds // Neighbour never checked, try autodiscovery $remote_device_id = autodiscovery_device($lldp['lldpRemSysName'], $lldp['lldpRemManAddr'], 'LLDP', $lldp['lldpRemSysDesc'], $device, $port); } if ($remote_device_id) { $if = $lldp['lldpRemPortDesc']; $id = trim($lldp['lldpRemPortId']); // lldpPortIdSubtype -> lldpPortId // interfaceAlias(1), -> ifAlias // portComponent(2), -> entPhysicalAlias // macAddress(3), -> ifPhysAddress // networkAddress(4), -> IP address // interfaceName(5), -> ifName // agentCircuitId(6), -> agent-local identifier of the circuit (defined in RFC 3046) (FIXME, not know) // local(7) -> ifIndex switch ($lldp['lldpRemPortIdSubtype']) { case 'interfaceAlias': $remote_port_id = dbFetchCell("SELECT `port_id` FROM `ports` WHERE (`ifAlias` = ? OR `ifDescr` = ? OR `port_label_short` = ?) AND `device_id` = ? AND `deleted` = ?", [$id, $if, $if, $remote_device_id, 0]); break; case 'interfaceName': // Try lldpRemPortId $query = 'SELECT `port_id` FROM `ports` WHERE (`ifName` = ? OR `ifDescr` = ? OR `port_label_short` = ?) AND `device_id` = ? AND `deleted` = ?'; $remote_port_id = dbFetchCell($query, [$id, $id, $id, $remote_device_id, 0]); if (!$remote_port_id && !safe_empty($if)) { // Try same by lldpRemPortDesc $remote_port_id = dbFetchCell($query, [$if, $if, $if, $remote_device_id, 0]); } break; case 'macAddress': $remote_port_id = get_port_id_by_mac($remote_device_id, $id); if (!$remote_port_id && !safe_empty($if)) { // Try same by lldpRemPortDesc $query = 'SELECT `port_id` FROM `ports` WHERE (`ifName` = ? OR `ifDescr` = ? OR `port_label_short` = ?) AND `device_id` = ? AND `deleted` = ?'; $remote_port_id = dbFetchCell($query, [$if, $if, $if, $remote_device_id, 0]); } break; case 'networkAddress': $ip_version = get_ip_version($id); if ($ip_version) { // Try by IP $peer_where = generate_query_values_and($remote_device_id, 'device_id'); // Additional filter for include self IPs // Fetch all devices with peer IP and filter by UP if ($ids = get_entity_ids_ip_by_network('port', $id, $peer_where)) { $remote_port_id = $ids[0]; //$port = get_port_by_id_cache($ids[0]); } } break; case 'local': // local not always ifIndex or FIXME (see: http://jira.observium.org/browse/OBSERVIUM-1716) if (!ctype_digit($id)) { // Not sure what should be if $id ifName and it just numeric $query = 'SELECT `port_id` FROM `ports` WHERE (`ifName` = ? OR `ifDescr` = ? OR `port_label_short` = ?) AND `device_id` = ? AND `deleted` = ?'; $remote_port_id = dbFetchCell($query, [$id, $id, $id, $remote_device_id, 0]); if (!$remote_port_id) { // Try same by lldpRemPortDesc $remote_port_id = dbFetchCell($query, [$if, $if, $if, $remote_device_id, 0]); } } case 'ifIndex': // These cases are handled by the ifDescr/ifIndex combination fallback below break; default: break; } if (!$remote_port_id && is_numeric($id)) { // Not found despite our attempts above - fall back to try matching with ifDescr/ifIndex $remote_port_id = dbFetchCell("SELECT `port_id` FROM `ports` WHERE (`ifIndex`= ? OR `ifDescr` = ?) AND `device_id` = ? AND `deleted` = ?", [$id, $if, $remote_device_id, 0]); } if (!$remote_port_id) { // Still not found? if ($lldp['lldpRemChassisIdSubtype'] === 'macAddress') { // Find the port by chassis MAC address, only use this if exactly 1 match is returned, otherwise we'd link wrongly - think switches with 1 global MAC on all ports. $remote_port_id = get_port_id_by_mac($remote_device_id, $lldp['lldpRemChassisId']); } else { // Last chance $query = 'SELECT `port_id` FROM `ports` WHERE (`ifName` IN (?, ?) OR `ifDescr` IN (?, ?) OR `port_label_short` IN (?, ?)) AND `device_id` = ? AND `deleted` = ?'; $remote_port_id = dbFetchCell($query, [$id, $if, $id, $if, $id, $if, $remote_device_id, 0]); } } // Still not found? Seems as incorrect remote device :/ if (!$remote_port_id) { print_debug("WARNING. Remote device found in db, but remote port not found. Seems as incorrect remote device association."); } } // FIXME. We can use lldpRemSysCapEnabled as platform, but they use BITS textual conversion: // LLDP-MIB::lldpRemSysCapEnabled.0.5.3 = BITS: 20 00 bridge(2) // LLDP-MIB::lldpRemSysCapEnabled.0.5.3 = "20 00 " $neighbour = [ 'remote_device_id' => $remote_device_id, 'remote_port_id' => $remote_port_id, 'remote_hostname' => $lldp['lldpRemSysName'], 'remote_port' => $lldp['lldpRemPortId'], 'remote_platform' => $lldp['lldpRemSysDesc'], 'remote_version' => $lldp['lldpRemSysVersion'], //NULL, 'remote_address' => $lldp['lldpRemManAddr'] ]; discover_neighbour($port, 'lldp', $neighbour); } // EOF