Observium_CE/includes/discovery/ip-addresses.inc.php

279 lines
12 KiB
PHP

<?php
/**
* Observium
*
* This file is part of Observium.
*
* @package observium
* @subpackage discovery
* @copyright (C) Adam Armstrong
*
*/
/**
* @var array $config
* @var array $device
* @var string $module
* @global array $ip_data
*/
global $ip_data;
$ip_data = ['ipv4' => [], 'ipv6' => []];
//$valid['ip-addresses'] = array();
$include_dir = 'includes/discovery/ip-addresses';
$include_order = 'default'; // Use MIBs from default os definitions by first!
include($config['install_dir'] . "/includes/include-dir-mib.inc.php");
foreach (get_device_mibs_permitted($device) as $mib) {
// Detect addresses by definitions
if (is_array($config['mibs'][$mib]['ip-address'])) {
print_cli_data_field($mib);
foreach ($config['mibs'][$mib]['ip-address'] as $oid_data) {
discover_ip_address_definition($device, $mib, $oid_data);
}
print_cli(PHP_EOL);
}
}
// Try discovery IP addresses in VRF SNMP contexts (currently actual only on Cisco Nexus)
if ($vrf_contexts = get_device_vrf_contexts($device)) { // SNMP VRF context discovered for device
// Keep original device array
$device_original = $device;
foreach ($vrf_contexts as $vrf_name => $snmp_virtual) {
print_message("Addresses in Virtual Routing: $vrf_name...");
$device = snmp_virtual_device($device_original, $snmp_virtual);
//$device['snmp_context'] = $snmp_context;
if (!$device['snmp_retries']) {
// force less retries on vrf requests.. if not set in db
$device['snmp_retries'] = 1;
}
$include_dir = 'includes/discovery/ip-addresses';
$include_order = 'default'; // Use MIBs from default os definitions by first!
include($config['install_dir'] . "/includes/include-dir-mib.inc.php");
}
// Clean
$device = $device_original;
unset($device_original);
}
// Process IP Addresses
$table_rows = [];
$check_networks = [];
foreach (['ipv4', 'ipv6'] as $ip_version) {
print_debug_vars($ip_data[$ip_version]);
// Caching old IP addresses table
$query = 'SELECT * FROM `' . $ip_version . '_addresses`
WHERE `device_id` = ?';
foreach (dbFetchRows($query, [$device['device_id']]) as $entry) {
if (safe_empty($entry['ifIndex'])) {
// Compatibility
$ifIndex = dbFetchCell('SELECT `ifIndex` FROM `ports` WHERE `port_id` = ? AND `deleted` = ?', [$entry['port_id'], 0]);
} else {
$ifIndex = $entry['ifIndex'];
}
$old_table[$ip_version][$ifIndex][$entry[$ip_version . '_address']] = $entry;
}
if (!safe_count($ip_data[$ip_version]) && !safe_count($old_table[$ip_version])) {
// Skip if walk and DB empty
continue;
}
// Process founded IP addresses
foreach ($ip_data[$ip_version] as $ifIndex => $addresses) {
$port = get_port_by_index_cache($device, $ifIndex);
if (is_array($port) && !$port['deleted']) {
$port_id = $port['port_id'];
} else {
// Allow to store IP addresses without associated port, but ifIndex available
// ie, Nortel/Avaya devices have hidden vlan ifIndexes
$port_id = '0';
}
print_debug_vars($port);
print_debug_vars($addresses);
foreach ($addresses as $ip_address => $entry) {
if ($ip_version === 'ipv4') {
// IPv4
$ip_prefix = $entry['prefix'];
$ip_origin = $entry['origin'];
$ip_compressed = $ip_address; // just for compat with IPv6
$ip_type = $entry['type'];
$addr = Net_IPv4 ::parseAddress($ip_address . '/' . $ip_prefix);
$ip_cidr = $addr -> bitmask;
$ip_network = $addr -> network . '/' . $ip_cidr;
$full_address = $ip_address . '/' . $ip_cidr;
$new_address = [
'port_id' => $port_id,
'ifIndex' => $ifIndex,
'device_id' => $device['device_id'],
'ipv4_address' => $ip_address,
'ipv4_binary' => inet_pton($ip_address),
'ipv4_prefixlen' => $ip_cidr,
'ipv4_type' => $ip_type
];
} else {
// IPv6
$ip_prefix = $entry['prefix'];
$ip_origin = $entry['origin'];
$ip_compressed = Net_IPv6 ::compress($ip_address, TRUE);
$full_address = $ip_compressed . '/' . $ip_prefix;
$ip_type = $entry['type'];
$ip_network = Net_IPv6 ::getNetmask($full_address) . '/' . $ip_prefix;
//$full_compressed = $ip_compressed.'/'.$ipv6_prefixlen;
$new_address = [
'port_id' => $port_id,
'ifIndex' => $ifIndex,
'device_id' => $device['device_id'],
'ipv6_address' => $ip_address,
'ipv6_binary' => inet_pton($ip_address),
'ipv6_compressed' => $ip_compressed,
'ipv6_prefixlen' => $ip_prefix,
'ipv6_type' => $ip_type,
'ipv6_origin' => $ip_origin
];
}
// VRFs
$sql_vrf = "SELECT `vrf_id` FROM `vrfs` WHERE `device_id` = ? AND `vrf_name` = ?";
if (!safe_empty($entry['vrf']) &&
$vrf_id = dbFetchCell($sql_vrf, [$device['device_id'], $entry['vrf']])) {
// VRF discovered as name in addresses discovery
$new_address['vrf_id'] = $vrf_id;
} elseif ($port_id && $port['ifVrf']) {
// Port associated with VRF in vrf discovery
$new_address['vrf_id'] = $port['ifVrf'];
}
// Check network
$ip_network_id = dbFetchCell('SELECT `' . $ip_version . '_network_id` FROM `' . $ip_version . '_networks` WHERE `' . $ip_version . '_network` = ?', [$ip_network]);
if (empty($ip_network_id)) {
// Add new network
$ip_network_id = dbInsert([$ip_version . '_network' => $ip_network], $ip_version . '_networks');
//echo('N');
}
$new_address[$ip_version . '_network_id'] = $ip_network_id;
// Add to display table
$table_rows[] = [$ifIndex, truncate($port['ifDescr'], 30), nicecase($ip_version), $full_address, $ip_network, $entry['type'], $ip_origin];
// Check IP in DB
$update_array = [];
if (isset($old_table[$ip_version][$ifIndex][$ip_address])) {
$old_address = &$old_table[$ip_version][$ifIndex][$ip_address];
$ip_address_id = $old_address[$ip_version . '_address_id'];
$params = array_diff(array_keys($old_address), ['device_id', $ip_version . '_address_id']);
foreach ($params as $param) {
if ($param === 'ipv6_binary') {
// Compare decoded binary IPv6 address
if (inet_ntop($old_address[$param]) != $new_address['ipv6_compressed']) {
//$update_array[$param] = $new_address[$param];
$update_array[$param] = [binary_to_hex($new_address[$param])];
}
} elseif ($param === 'ipv4_binary') {
// Compare decoded binary IPv4 address
if (inet_ntop($old_address[$param]) != $new_address['ipv4_address']) {
//$update_array[$param] = $new_address[$param];
$update_array[$param] = [binary_to_hex($new_address[$param])];
}
} else {
// All other params as string
if ($old_address[$param] != $new_address[$param]) {
if (in_array($param, ['vrf_id', 'ifIndex', $ip_version . '_type']) && !strlen($new_address[$param])) {
$update_array[$param] = ['NULL'];
} else {
$update_array[$param] = $new_address[$param];
}
}
}
}
$update_count = count($update_array);
if ($update_count === 1 && (isset($update_array['ipv4_binary']) || isset($update_array['ipv6_binary']))) {
// Silent update binary address after upgrade
dbUpdate($update_array, $ip_version . '_addresses', '`' . $ip_version . '_address_id` = ?', [$old_address[$ip_version . '_address_id']]);
$GLOBALS['module_stats'][$module]['unchanged']++;
} elseif ($update_count) {
// Updated
dbUpdate($update_array, $ip_version . '_addresses', '`' . $ip_version . '_address_id` = ?', [$old_address[$ip_version . '_address_id']]);
if (!$port_id) {
log_event("IP address changed: $ip_compressed/" . $old_address[$ip_version . '_prefixlen'] . " -> $full_address", $device, 'device', $device['device_id']);
} elseif (isset($update_array['port_id'])) {
// Address moved to another port
log_event("IP address removed: $ip_compressed/" . $old_address[$ip_version . '_prefixlen'], $device, 'port', $old_address['port_id']);
log_event("IP address added: $full_address", $device, 'port', $port_id);
} else {
// Changed prefix/cidr
log_event("IP address changed: $ip_compressed/" . $old_address[$ip_version . '_prefixlen'] . " -> $full_address", $device, 'port', $port_id);
}
$GLOBALS['module_stats'][$module]['updated']++; //echo "U";
$check_networks[$ip_version][$ip_network_id] = 1;
} else {
// Not changed
$GLOBALS['module_stats'][$module]['unchanged']++; //echo ".";
}
} else {
// New IP
$update_array = $new_address;
$ip_address_id = dbInsert($update_array, $ip_version . '_addresses');
if ($port_id) {
log_event("IP address added: $full_address", $device, 'port', $port_id);
} else {
log_event("IP address added: $full_address", $device, 'device', $device['device_id']);
}
$GLOBALS['module_stats'][$module]['added']++; //echo "+";
}
$valid[$ip_version][$ip_address_id] = $full_address . ':' . $port_id;
}
}
// Refetch and clean IP addresses from DB
foreach (dbFetchRows($query, [$device['device_id']]) as $entry) {
$ip_address_id = $entry[$ip_version . '_address_id'];
if (!isset($valid[$ip_version][$ip_address_id])) {
$full_address = ($ip_version === 'ipv4' ? $entry['ipv4_address'] : $entry['ipv6_compressed']);
$full_address .= '/' . $entry[$ip_version . '_prefixlen'];
// Delete IP
dbDelete($ip_version . '_addresses', '`' . $ip_version . '_address_id` = ?', [$ip_address_id]);
if ($port_id) {
log_event("IP address removed: $full_address", $device, 'port', $entry['port_id']);
} else {
log_event("IP address removed: $full_address", $device, 'device', $entry['device_id']);
}
$GLOBALS['module_stats'][$module]['deleted']++; //echo "-";
$check_networks[$ip_version][$entry[$ip_version . '_network_id']] = 1;
}
}
// Clean networks
foreach ($check_networks[$ip_version] as $ip_network_id => $n) {
//$count = dbFetchCell('SELECT COUNT(*) FROM `'.$ip_version.'_addresses` WHERE `'.$ip_version.'_network_id` = ?', array($ip_network_id));
//if (empty($count))
if (!dbExist($ip_version . '_addresses', '`' . $ip_version . '_network_id` = ?', [$ip_network_id])) {
dbDelete($ip_version . '_networks', '`' . $ip_version . '_network_id` = ?', [$ip_network_id]);
//echo('n');
}
}
}
$table_headers = ['%WifIndex%n', '%WifDescr%n', '%WIP: Version%n', '%WAddress%n', '%WNetwork%n', '%WType%n', '%WOrigin%n'];
print_cli_table($table_rows, $table_headers);
// Clean
unset($ip_data, $check_networks, $check_ipv6_mib, $update_array, $old_table, $table_rows, $table_headers);
// EOF