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';
# 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 $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";
@ -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 $IFOPERSTATUS = ".1.3.6.1.2.1.2.2.1.8";
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 $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 $IPNETTOMEDIAPHYSADDRESS = ".1.3.6.1.2.1.4.22.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 $IPROUTENEXTHOP = ".1.3.6.1.2.1.4.21.1.7";
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.
arp_cache => {},
# Found children.
children => {},
# Working SNMP community for each device.
community_cache => {},
@ -289,20 +294,9 @@ sub snmp_discovery($$) {
# Find interfaces for the device.
$self->find_ifaces($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);
}
# Check remote ARP caches.
$self->remote_arp($device);
}
}
@ -411,6 +405,9 @@ sub find_ifaces($$) {
next unless ($if_index =~ /^[0-9]+$/);
# Ignore virtual interfaces.
next if ($self->get_if_type($device, $if_index) eq '53');
# Get the MAC.
my $mac = $self->get_if_mac($device, $if_index);
next unless (defined($mac) && $mac ne '');
@ -636,7 +633,7 @@ sub get_if_ip($$$) {
my @output = $self->snmp_get($device, $IPADENTIFINDEX);
foreach my $line (@output) {
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 '';
@ -657,6 +654,18 @@ sub get_if_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.
########################################################################################
@ -858,6 +867,20 @@ sub guess_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.
########################################################################################
@ -949,6 +972,7 @@ sub mark_connected($$;$$$) {
# A parent-child relationship is always created to help complete the map with
# layer 3 information.
$self->{'parents'}->{$child} = $parent;
$self->{'children'}->{$parent} = $child;
$self->call('set_parent', $child, $parent);
}
}
@ -999,6 +1023,54 @@ sub snmp_responds($$) {
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.
##############################################################################
@ -1152,6 +1224,9 @@ sub scan($) {
$self->call('message', "[1/5] Scanning the network...", 3);
$self->scan_subnet();
# Read the local ARP cache.
$self->local_arp();
# Get a list of found hosts.
my @hosts = @{$self->get_hosts()};
if (scalar(@hosts) > 0 && $self->{'parent_detection'} == 1) {
@ -1173,7 +1248,7 @@ sub scan($) {
foreach my $host (@hosts) {
$self->call('update_progress', $progress);
$progress += $step;
next if ($self->has_parent($host));
next if ($self->has_parent($host) || $self->has_children($host));
$self->traceroute_connectivity($host);
}