Improvements to the Pandora FMS Recon Server.

- Ignore virtual interfaces.
- Improved scan of ARP caches.
This commit is contained in:
Ramon Novoa 2018-05-21 15:03:54 +02:00
parent 1f3e09ae14
commit 8bda3751df
1 changed files with 92 additions and 17 deletions

View File

@ -19,6 +19,7 @@ use Socket qw/inet_aton/;
my $DEVNULL = ($^O eq 'MSWin32') ? '/Nul' : '/dev/null'; my $DEVNULL = ($^O eq 'MSWin32') ? '/Nul' : '/dev/null';
# Some useful OIDs. # Some useful OIDs.
our $ATPHYSADDRESS = ".1.3.6.1.2.1.3.1.1.2";
our $DOT1DBASEBRIDGEADDRESS = ".1.3.6.1.2.1.17.1.1.0"; our $DOT1DBASEBRIDGEADDRESS = ".1.3.6.1.2.1.17.1.1.0";
our $DOT1DBASEPORTIFINDEX = ".1.3.6.1.2.1.17.1.4.1.2"; our $DOT1DBASEPORTIFINDEX = ".1.3.6.1.2.1.17.1.4.1.2";
our $DOT1DTPFDBADDRESS = ".1.3.6.1.2.1.17.4.3.1.1"; our $DOT1DTPFDBADDRESS = ".1.3.6.1.2.1.17.4.3.1.1";
@ -30,11 +31,12 @@ our $IFINDEX = ".1.3.6.1.2.1.2.2.1.1";
our $IFINOCTECTS = ".1.3.6.1.2.1.2.2.1.10"; our $IFINOCTECTS = ".1.3.6.1.2.1.2.2.1.10";
our $IFOPERSTATUS = ".1.3.6.1.2.1.2.2.1.8"; our $IFOPERSTATUS = ".1.3.6.1.2.1.2.2.1.8";
our $IFOUTOCTECTS = ".1.3.6.1.2.1.2.2.1.16"; our $IFOUTOCTECTS = ".1.3.6.1.2.1.2.2.1.16";
our $IFTYPE = ".1.3.6.1.2.1.2.2.1.3";
our $IPENTADDR = ".1.3.6.1.2.1.4.20.1.1"; our $IPENTADDR = ".1.3.6.1.2.1.4.20.1.1";
our $IFNAME = ".1.3.6.1.2.1.31.1.1.1.1"; our $IFNAME = ".1.3.6.1.2.1.31.1.1.1.1";
our $IFPHYSADDRESS = ".1.3.6.1.2.1.2.2.1.6"; our $IFPHYSADDRESS = ".1.3.6.1.2.1.2.2.1.6";
our $IPNETTOMEDIAPHYSADDRESS = ".1.3.6.1.2.1.4.22.1.2";
our $IPADENTIFINDEX = ".1.3.6.1.2.1.4.20.1.2"; our $IPADENTIFINDEX = ".1.3.6.1.2.1.4.20.1.2";
our $IPNETTOMEDIAPHYSADDRESS = ".1.3.6.1.2.1.4.22.1.2";
our $IPROUTEIFINDEX = ".1.3.6.1.2.1.4.21.1.2"; our $IPROUTEIFINDEX = ".1.3.6.1.2.1.4.21.1.2";
our $IPROUTENEXTHOP = ".1.3.6.1.2.1.4.21.1.7"; our $IPROUTENEXTHOP = ".1.3.6.1.2.1.4.21.1.7";
our $IPROUTETYPE = ".1.3.6.1.2.1.4.21.1.8"; our $IPROUTETYPE = ".1.3.6.1.2.1.4.21.1.8";
@ -88,6 +90,9 @@ sub new {
# Keep our own ARP cache to connect hosts to switches/routers. # Keep our own ARP cache to connect hosts to switches/routers.
arp_cache => {}, arp_cache => {},
# Found children.
children => {},
# Working SNMP community for each device. # Working SNMP community for each device.
community_cache => {}, community_cache => {},
@ -290,19 +295,8 @@ sub snmp_discovery($$) {
# Find interfaces for the device. # Find interfaces for the device.
$self->find_ifaces($device); $self->find_ifaces($device);
# Try to learn more MAC addresses from the device's ARP cache. # Check remote ARP caches.
my @output = $self->snmp_get($device, $IPNETTOMEDIAPHYSADDRESS); $self->remote_arp($device);
foreach my $line (@output) {
next unless ($line =~ /^$IPNETTOMEDIAPHYSADDRESS.\d+.(\S+)\s+=\s+\S+:\s+(.*)$/);
my ($ip_addr, $mac_addr) = ($1, $2);
# Skip broadcast, net and local addresses.
next if ($ip_addr =~ m/\.255$|\.0$|127\.0\.0\.1$/);
$mac_addr = parse_mac($mac_addr);
$self->add_mac($mac_addr, $ip_addr);
$self->call('message', "Found MAC $mac_addr for host $ip_addr in the ARP cache of host $device.", 5);
}
} }
} }
@ -411,6 +405,9 @@ sub find_ifaces($$) {
next unless ($if_index =~ /^[0-9]+$/); next unless ($if_index =~ /^[0-9]+$/);
# Ignore virtual interfaces.
next if ($self->get_if_type($device, $if_index) eq '53');
# Get the MAC. # Get the MAC.
my $mac = $self->get_if_mac($device, $if_index); my $mac = $self->get_if_mac($device, $if_index);
next unless (defined($mac) && $mac ne ''); next unless (defined($mac) && $mac ne '');
@ -636,7 +633,7 @@ sub get_if_ip($$$) {
my @output = $self->snmp_get($device, $IPADENTIFINDEX); my @output = $self->snmp_get($device, $IPADENTIFINDEX);
foreach my $line (@output) { foreach my $line (@output) {
chomp ($line); chomp ($line);
return $1 if ($line =~ m/^IPADENTIFINDEX.(\S+)\s+=\s+\S+:\s+$if_index$/); return $1 if ($line =~ m/^$IPADENTIFINDEX.(\S+)\s+=\s+\S+:\s+$if_index$/);
} }
return ''; return '';
@ -657,6 +654,18 @@ sub get_if_mac($$$) {
return $mac; return $mac;
} }
########################################################################################
# Returns the type of the given interface (by index).
########################################################################################
sub get_if_type($$$) {
my ($self, $device, $if_index) = @_;
my $type = $self->snmp_get_value($device, "$IFTYPE.$if_index");
return '' unless defined($type);
return $type;
}
######################################################################################## ########################################################################################
# Get an IP address from the ARP cache given the MAC address. # Get an IP address from the ARP cache given the MAC address.
######################################################################################## ########################################################################################
@ -858,6 +867,20 @@ sub guess_device_type($$) {
$self->set_device_type($device, $device_type); $self->set_device_type($device, $device_type);
} }
########################################################################################
# Return 1 if the given device has children.
########################################################################################
sub has_children($$) {
my ($self, $device) = @_;
# Check for aliases!
$device = $self->{'aliases'}->{$device} if defined($self->{'aliases'}->{$device});
return 1 if (defined($self->{'children'}->{$device}));
return 0;
}
######################################################################################## ########################################################################################
# Return 1 if the given device has a parent. # Return 1 if the given device has a parent.
######################################################################################## ########################################################################################
@ -949,6 +972,7 @@ sub mark_connected($$;$$$) {
# A parent-child relationship is always created to help complete the map with # A parent-child relationship is always created to help complete the map with
# layer 3 information. # layer 3 information.
$self->{'parents'}->{$child} = $parent; $self->{'parents'}->{$child} = $parent;
$self->{'children'}->{$parent} = $child;
$self->call('set_parent', $child, $parent); $self->call('set_parent', $child, $parent);
} }
} }
@ -999,6 +1023,54 @@ sub snmp_responds($$) {
return 0; return 0;
} }
##############################################################################
# Parse the local ARP cache.
##############################################################################
sub local_arp($) {
my ($self) = @_;
my @output = `arp -an 2>/dev/null`;
foreach my $line (@output) {
next unless ($line =~ m/\((\S+)\) at ([0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+)/);
$self->add_mac(parse_mac($2), $1);
}
}
##############################################################################
# Parse remote SNMP ARP caches.
##############################################################################
sub remote_arp($$) {
my ($self, $device) = @_;
# Try to learn more MAC addresses from the device's ARP cache.
my @output = $self->snmp_get($device, $IPNETTOMEDIAPHYSADDRESS);
foreach my $line (@output) {
next unless ($line =~ /^$IPNETTOMEDIAPHYSADDRESS\.\d+\.(\S+)\s+=\s+\S+:\s+(.*)$/);
my ($ip_addr, $mac_addr) = ($1, $2);
# Skip broadcast, net and local addresses.
next if ($ip_addr =~ m/\.255$|\.0$|127\.0\.0\.1$/);
$mac_addr = parse_mac($mac_addr);
$self->add_mac($mac_addr, $ip_addr);
$self->call('message', "Found MAC $mac_addr for host $ip_addr in the ARP cache of host $device.", 5);
}
# Look in atPhysAddress for MAC addresses too.
@output = $self->snmp_get($device, $ATPHYSADDRESS);
foreach my $line (@output) {
next unless ($line =~ m/^$ATPHYSADDRESS\.\d+\.\d+\.(\S+)\s+=\s+\S+:\s+(.*)$/);
my ($ip_addr, $mac_addr) = ($1, $2);
# Skip broadcast, net and local addresses.
next if ($ip_addr =~ m/\.255$|\.0$|127\.0\.0\.1$/);
$mac_addr = parse_mac($mac_addr);
$self->add_mac($mac_addr, $ip_addr);
$self->call('message', "Found MAC $mac_addr for host $ip_addr in the ARP cache (atPhysAddress) of host $device.", 5);
}
}
############################################################################## ##############################################################################
# Ping the given host. Returns 1 if the host is alive, 0 otherwise. # Ping the given host. Returns 1 if the host is alive, 0 otherwise.
############################################################################## ##############################################################################
@ -1152,6 +1224,9 @@ sub scan($) {
$self->call('message', "[1/5] Scanning the network...", 3); $self->call('message', "[1/5] Scanning the network...", 3);
$self->scan_subnet(); $self->scan_subnet();
# Read the local ARP cache.
$self->local_arp();
# Get a list of found hosts. # Get a list of found hosts.
my @hosts = @{$self->get_hosts()}; my @hosts = @{$self->get_hosts()};
if (scalar(@hosts) > 0 && $self->{'parent_detection'} == 1) { if (scalar(@hosts) > 0 && $self->{'parent_detection'} == 1) {
@ -1173,7 +1248,7 @@ sub scan($) {
foreach my $host (@hosts) { foreach my $host (@hosts) {
$self->call('update_progress', $progress); $self->call('update_progress', $progress);
$progress += $step; $progress += $step;
next if ($self->has_parent($host)); next if ($self->has_parent($host) || $self->has_children($host));
$self->traceroute_connectivity($host); $self->traceroute_connectivity($host);
} }