Several improvements to the SNMP layer 2 recon script.
This commit is contained in:
parent
e2937338aa
commit
cfd7590efc
|
@ -27,6 +27,9 @@ my $ALLIFACES = '';
|
|||
# Keep our own ARP cache to connect hosts to switches/routers.
|
||||
my %ARP_CACHE;
|
||||
|
||||
# IP address of a host given the MAC of one of its interfaces.
|
||||
my %IF_CACHE;
|
||||
|
||||
# Default configuration values.
|
||||
my $OSNAME = $^O;
|
||||
my %CONF;
|
||||
|
@ -36,6 +39,7 @@ if ($OSNAME eq "freebsd") {
|
|||
'nmap' => '/usr/local/bin/nmap',
|
||||
'pandora_path' => '/usr/local/etc/pandora/pandora_server.conf',
|
||||
'icmp_checks' => 1,
|
||||
'icmp_packets' => 1,
|
||||
'networktimeout' => 2,
|
||||
'snmp_checks' => 2,
|
||||
'snmp_timeout' => 2,
|
||||
|
@ -48,6 +52,7 @@ if ($OSNAME eq "freebsd") {
|
|||
'nmap' => '/usr/bin/nmap',
|
||||
'pandora_path' => '/etc/pandora/pandora_server.conf',
|
||||
'icmp_checks' => 1,
|
||||
'icmp_packets' => 1,
|
||||
'networktimeout' => 2,
|
||||
'snmp_timeout' => 2,
|
||||
'recon_timing_template' => 3,
|
||||
|
@ -72,10 +77,15 @@ my $DBH;
|
|||
my $GROUP_ID;
|
||||
|
||||
# Devices by type.
|
||||
my %HOSTS;
|
||||
my @HOSTS;
|
||||
my @ROUTERS;
|
||||
my @SWITCHES;
|
||||
|
||||
# Switch to switch connections. Used to properly connect hosts
|
||||
# that are connected to a switch wich is in turn connected to another switch,
|
||||
# since the hosts will show up in the latter's switch AFT too.
|
||||
my %SWITCH_TO_SWITCH;
|
||||
|
||||
# MAC addresses.
|
||||
my %MAC;
|
||||
|
||||
|
@ -145,7 +155,7 @@ sub mac_to_dec($) {
|
|||
my $mac = shift;
|
||||
|
||||
my $dec_mac = '';
|
||||
my @elements = split(/ /, $mac);
|
||||
my @elements = split(/:/, $mac);
|
||||
foreach my $element (@elements) {
|
||||
$dec_mac .= unpack('s', pack 's', hex($element)) . '.'
|
||||
}
|
||||
|
@ -154,6 +164,42 @@ sub mac_to_dec($) {
|
|||
return $dec_mac;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
# Make sure all MAC addresses are in the same format (00 11 22 33 44 55 66).
|
||||
########################################################################################
|
||||
sub parse_mac($) {
|
||||
my ($mac) = @_;
|
||||
|
||||
# Remove leading and trailing whitespaces.
|
||||
$mac =~ s/(^\s+)|(\s+$)//g;
|
||||
|
||||
# Replace whitespaces and dots with colons.
|
||||
$mac =~ s/\s+|\./:/g;
|
||||
|
||||
# Convert hex digits to uppercase.
|
||||
$mac =~ s/([a-f])/\U$1/g;
|
||||
|
||||
# Add a leading 0 to single digits.
|
||||
$mac =~ s/^([0-9A-F]):/0$1:/g;
|
||||
$mac =~ s/:([0-9A-F]):/:0$1:/g;
|
||||
$mac =~ s/:([0-9A-F])$/:0$1/g;
|
||||
|
||||
return $mac;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
# Returns 1 if the two given MAC addresses are the same.
|
||||
########################################################################################
|
||||
sub mac_matches($$) {
|
||||
my ($mac_1, $mac_2) = @_;
|
||||
|
||||
if (parse_mac($mac_1) eq parse_mac($mac_2)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
# Returns 1 if the device belongs to one of the scanned subnets.
|
||||
########################################################################################
|
||||
|
@ -280,8 +326,9 @@ sub get_if_from_mac($$$) {
|
|||
my @output = snmp_get($device, $community, $IFPHYSADDRESS);
|
||||
foreach my $line (@output) {
|
||||
chomp($line);
|
||||
next unless $line =~ /^$IFPHYSADDRESS.(\S+)\s+=\s+\S+:\s+$mac$/;
|
||||
my $if_index = $1;
|
||||
next unless $line =~ /^$IFPHYSADDRESS.(\S+)\s+=\s+\S+:\s+(.*)$/;
|
||||
my ($if_index, $if_mac) = ($1, $2);
|
||||
next unless (mac_matches($mac, $if_mac) == 1);
|
||||
|
||||
# Get the name of the interface associated to the port.
|
||||
my $if_name = snmp_get_value($device, $community, "$IFNAME.$if_index");
|
||||
|
@ -310,7 +357,7 @@ sub get_if_from_aft($$$) {
|
|||
|
||||
# Get the interface name.
|
||||
my $if_name = snmp_get_value($switch, $COMMUNITIES{$switch}, "$IFNAME.$if_index");
|
||||
return '' unless defined($if_name);
|
||||
return "if$if_index" unless defined($if_name);
|
||||
|
||||
$if_name =~ s/"//g;
|
||||
return $if_name;
|
||||
|
@ -342,8 +389,7 @@ sub get_if_mac($$$) {
|
|||
return '' unless defined($mac);
|
||||
|
||||
# Clean-up the MAC address.
|
||||
$mac =~ s/ /:/g;
|
||||
chop($mac);
|
||||
$mac = parse_mac($mac);
|
||||
|
||||
return $mac;
|
||||
}
|
||||
|
@ -415,6 +461,7 @@ sub arp_cache_discovery {
|
|||
foreach my $line (@output) {
|
||||
next unless ($line =~ /^$IPNETTOMEDIAPHYSADDRESS.\d+.(\S+)\s+=\s+\S+:\s+(.*)$/);
|
||||
my ($ip_addr, $mac_addr) = ($1, $2);
|
||||
$mac_addr = parse_mac($mac_addr);
|
||||
|
||||
# Save the mac to connect hosts to switches/routers.
|
||||
$ARP_CACHE{$mac_addr} = $ip_addr;
|
||||
|
@ -428,7 +475,7 @@ sub arp_cache_discovery {
|
|||
if ($device_type eq 'host' || $device_type eq 'printer') {
|
||||
|
||||
# Hosts are indexed to help find router/switch to host connectivity.
|
||||
$HOSTS{$device} = '';
|
||||
push(@HOSTS, $device);
|
||||
}
|
||||
elsif ($device_type eq 'switch') {
|
||||
push(@SWITCHES, $device);
|
||||
|
@ -457,7 +504,7 @@ sub find_synonyms($$$) {
|
|||
|
||||
# There is no need to access switches or routers from different IP addresses.
|
||||
if ($device_type eq 'host' || $device_type eq 'printer') {
|
||||
$HOSTS{$ip_address} = '';
|
||||
push(@HOSTS, $device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -534,17 +581,34 @@ sub guess_device_type($$) {
|
|||
########################################################################################
|
||||
sub switch_to_switch_connectivity($$) {
|
||||
my ($switch_1, $switch_2) = @_;
|
||||
my (%mac_temp, @aft_temp);
|
||||
|
||||
# Make sure both switches respond to SNMP.
|
||||
return unless defined($COMMUNITIES{$switch_1} && $COMMUNITIES{$switch_2});
|
||||
|
||||
# Get the list of MAC addresses of each switch.
|
||||
my %mac_1 = snmp_get_value_hash($switch_1, $COMMUNITIES{$switch_1}, $IFPHYSADDRESS);
|
||||
my %mac_2 = snmp_get_value_hash($switch_2, $COMMUNITIES{$switch_2}, $IFPHYSADDRESS);
|
||||
my %mac_1;
|
||||
%mac_temp = snmp_get_value_hash($switch_1, $COMMUNITIES{$switch_1}, $IFPHYSADDRESS);
|
||||
foreach my $mac (keys(%mac_temp)) {
|
||||
$mac_1{parse_mac($mac)} = '';
|
||||
}
|
||||
my %mac_2;
|
||||
%mac_temp = snmp_get_value_hash($switch_2, $COMMUNITIES{$switch_2}, $IFPHYSADDRESS);
|
||||
foreach my $mac (keys(%mac_temp)) {
|
||||
$mac_2{parse_mac($mac)} = '';
|
||||
}
|
||||
|
||||
# Get the address forwarding table (AFT) of each switch.
|
||||
my @aft_1 = snmp_get_value_array($switch_1, $COMMUNITIES{$switch_1}, $DOT1DTPFDBADDRESS);
|
||||
my @aft_2 = snmp_get_value_array($switch_2, $COMMUNITIES{$switch_2}, $DOT1DTPFDBADDRESS);
|
||||
my @aft_1;
|
||||
@aft_temp = snmp_get_value_array($switch_1, $COMMUNITIES{$switch_1}, $DOT1DTPFDBADDRESS);
|
||||
foreach my $mac (@aft_temp) {
|
||||
push(@aft_1, parse_mac($mac));
|
||||
}
|
||||
my @aft_2;
|
||||
@aft_temp = snmp_get_value_array($switch_2, $COMMUNITIES{$switch_2}, $DOT1DTPFDBADDRESS);
|
||||
foreach my $mac (@aft_temp) {
|
||||
push(@aft_2, parse_mac($mac));
|
||||
}
|
||||
|
||||
# Search for matching entries.
|
||||
foreach my $aft_mac_1 (@aft_1) {
|
||||
|
@ -552,9 +616,15 @@ sub switch_to_switch_connectivity($$) {
|
|||
foreach my $aft_mac_2 (@aft_2) {
|
||||
if (defined($mac_1{$aft_mac_2})) {
|
||||
my $if_name_1 = get_if_from_aft($switch_1, $COMMUNITIES{$switch_1}, $aft_mac_1);
|
||||
next unless ($if_name_1) ne '';
|
||||
my $if_name_2 = get_if_from_aft($switch_2, $COMMUNITIES{$switch_2}, $aft_mac_2);
|
||||
next unless ($if_name_2) ne '';
|
||||
message("Switch $switch_1 (if $if_name_1) is connected to switch $switch_2 (if $if_name_2).");
|
||||
connect_pandora_agents($switch_1, $if_name_1, $switch_2, $if_name_2);
|
||||
|
||||
# Mark switch to switch connections.
|
||||
$SWITCH_TO_SWITCH{"$switch_1$if_name_1"} = 1;
|
||||
$SWITCH_TO_SWITCH{"$switch_2$if_name_2"} = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -567,15 +637,24 @@ sub switch_to_switch_connectivity($$) {
|
|||
########################################################################################
|
||||
sub router_to_switch_connectivity($$) {
|
||||
my ($router, $switch) = @_;
|
||||
my (%mac_temp, @aft_temp);
|
||||
|
||||
# Make sure both routers respond to SNMP.
|
||||
return unless defined($COMMUNITIES{$router} && $COMMUNITIES{$switch});
|
||||
|
||||
# Get the list of MAC addresses of the router.
|
||||
my %mac_router = snmp_get_value_hash($router, $COMMUNITIES{$router}, $IFPHYSADDRESS);
|
||||
my %mac_router;
|
||||
%mac_temp = snmp_get_value_hash($router, $COMMUNITIES{$router}, $IFPHYSADDRESS);
|
||||
foreach my $mac (keys(%mac_temp)) {
|
||||
$mac_router{parse_mac($mac)} = '';
|
||||
}
|
||||
|
||||
# Get the address forwarding table (AFT) of the switch.
|
||||
my @aft = snmp_get_value_array($switch, $COMMUNITIES{$switch}, $DOT1DTPFDBADDRESS);
|
||||
my @aft;
|
||||
@aft_temp = snmp_get_value_array($switch, $COMMUNITIES{$switch}, $DOT1DTPFDBADDRESS);
|
||||
foreach my $mac (@aft_temp) {
|
||||
push(@aft, parse_mac($mac));
|
||||
}
|
||||
|
||||
# Search for matching entries in the AFT.
|
||||
foreach my $aft_mac (@aft) {
|
||||
|
@ -586,9 +665,14 @@ sub router_to_switch_connectivity($$) {
|
|||
|
||||
# Get the switch interface.
|
||||
my $switch_if_name = get_if_from_aft($switch, $COMMUNITIES{$switch}, $aft_mac);
|
||||
next unless ($switch_if_name ne '');
|
||||
|
||||
message("Router $router (if $router_if_name) is connected to switch $switch (if $switch_if_name).");
|
||||
connect_pandora_agents($router, $router_if_name, $switch, $switch_if_name);
|
||||
|
||||
# Mark connections in case the routers are switches too.
|
||||
$SWITCH_TO_SWITCH{"$switch$switch_if_name"} = 1;
|
||||
$SWITCH_TO_SWITCH{"$router$router_if_name"} = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -616,6 +700,10 @@ sub router_to_router_connectivity($$) {
|
|||
my $if_2 = get_if_from_ip($router_2, $COMMUNITIES{$router_2}, $ip_addr_1);
|
||||
message("Router $ip_addr_1 (if $if_2) is connected to router $ip_addr_2 (if $if_2).");
|
||||
connect_pandora_agents($router_1, $if_1, $router_2, $if_2);
|
||||
|
||||
# Mark connections in case the routers are switches too.
|
||||
$SWITCH_TO_SWITCH{"$router_1$if_1"} = 1;
|
||||
$SWITCH_TO_SWITCH{"$router_2$if_2"} = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -635,15 +723,25 @@ sub host_connectivity($) {
|
|||
# Get the address forwarding table (AFT) of the device.
|
||||
my @aft = snmp_get_value_array($device, $COMMUNITIES{$device}, $DOT1DTPFDBADDRESS);
|
||||
foreach my $mac (@aft) {
|
||||
next unless defined ($ARP_CACHE{$mac});
|
||||
my $host = $ARP_CACHE{$mac};
|
||||
next unless defined ($HOSTS{$host});
|
||||
$mac = parse_mac($mac);
|
||||
my $host;
|
||||
if (defined ($ARP_CACHE{$mac})) {
|
||||
$host = $ARP_CACHE{$mac};
|
||||
} elsif (defined ($IF_CACHE{$mac})) {
|
||||
$host = $IF_CACHE{$mac};
|
||||
} else {
|
||||
next;
|
||||
}
|
||||
next unless defined ($VISITED_DEVICES{$host});
|
||||
my $device_if_name = get_if_from_aft($device, $COMMUNITIES{$device}, $mac);
|
||||
next unless ($device_if_name ne '');
|
||||
my $host_if_name = defined($COMMUNITIES{$host}) ? get_if_from_mac($host, $COMMUNITIES{$host}, $mac) : '';
|
||||
if ($VISITED_DEVICES{$device}->{'type'} eq 'router') {
|
||||
next if defined ($SWITCH_TO_SWITCH{"$device$device_if_name"}); # The switch is probably connected to another router/switch.
|
||||
message("Host $host " . ($host_if_name ne '' ? "(if $host_if_name)" : '') . " is connected to router $device (if $device_if_name).");
|
||||
}
|
||||
elsif ($VISITED_DEVICES{$device}->{'type'} eq 'switch') {
|
||||
next if defined ($SWITCH_TO_SWITCH{"$device$device_if_name"}); # The switch is probably connected to another switch.
|
||||
message("Host $host " . ($host_if_name ne '' ? "(if $host_if_name)" : '') . " is connected to switch $device (if $device_if_name).");
|
||||
}
|
||||
else {
|
||||
|
@ -722,16 +820,19 @@ sub create_pandora_agent($) {
|
|||
next unless $if_status == 1;
|
||||
}
|
||||
|
||||
# Get the name of the network interface.
|
||||
my $if_name = snmp_get_value($device, $COMMUNITIES{$device}, "$IFNAME.$if_index");
|
||||
next unless defined($if_name);
|
||||
$if_name =~ s/"//g;
|
||||
|
||||
# Fill the module description with the IP and MAC addresses.
|
||||
my $mac = get_if_mac($device, $COMMUNITIES{$device}, $if_index);
|
||||
my $ip = get_if_ip($device, $COMMUNITIES{$device}, $if_index);
|
||||
my $if_desc = ($mac ne '' ? "MAC $mac " : '') . ($ip ne '' ? "IP $ip" : '');
|
||||
|
||||
# Fill the interface cache.
|
||||
$IF_CACHE{$mac} = $ip;
|
||||
|
||||
# Get the name of the network interface.
|
||||
my $if_name = snmp_get_value($device, $COMMUNITIES{$device}, "$IFNAME.$if_index");
|
||||
$if_name = "if$if_index" unless defined ($if_name);
|
||||
$if_name =~ s/"//g;
|
||||
|
||||
# Check whether the module already exists.
|
||||
my $module_id = get_agent_module_id($DBH, "ifOperStatus_${if_name}", $agent_id);
|
||||
next if ($module_id > 0);
|
||||
|
@ -833,9 +934,8 @@ sub connect_pandora_agents($$$$) {
|
|||
db_do($DBH, 'INSERT INTO tmodule_relationship (`module_a`, `module_b`, `id_rt`) VALUES(?, ?, ?)', $module_id_1, $module_id_2, $TASK_ID);
|
||||
}
|
||||
|
||||
# Unset parents (otherwise the map will look broken).
|
||||
db_do($DBH, 'UPDATE tagente SET id_parent=0 WHERE id_agente=?', $agent_1->{'id_agente'});
|
||||
db_do($DBH, 'UPDATE tagente SET id_parent=0 WHERE id_agente=?', $agent_2->{'id_agente'});
|
||||
# Update parents.
|
||||
db_do($DBH, 'UPDATE tagente SET id_parent=? WHERE id_agente=?', $agent_1->{'id_agente'}, $agent_2->{'id_agente'});
|
||||
}
|
||||
|
||||
|
||||
|
@ -990,6 +1090,10 @@ else {
|
|||
my @scanned_hosts = $np->all_hosts();
|
||||
foreach my $host (@scanned_hosts) {
|
||||
next unless defined($host->addr()) and defined($host->status()) and ($host->status() eq 'up');
|
||||
|
||||
# Make sure the host is up (nmap gives false positives!).
|
||||
next if (pandora_ping(\%CONF, $host->addr(), 1, 1) == 0);
|
||||
|
||||
arp_cache_discovery($host->addr());
|
||||
}
|
||||
}
|
||||
|
@ -1028,10 +1132,10 @@ update_recon_task($DBH, $TASK_ID, 75);
|
|||
|
||||
# Find switch/router to host connections.
|
||||
message("[6/6] Finding switch/router to end host connectivity...");
|
||||
foreach my $device ((@ROUTERS, @SWITCHES)) {
|
||||
foreach my $device (@ROUTERS, @SWITCHES, @HOSTS) {
|
||||
host_connectivity($device);
|
||||
}
|
||||
foreach my $host (keys(%HOSTS)) {
|
||||
foreach my $host (@HOSTS) {
|
||||
next unless (ref($VISITED_DEVICES{$host}) eq 'HASH'); # Skip aliases.
|
||||
next if ($VISITED_DEVICES{$host}->{'connected'} == 1); # Skip already connected hosts.
|
||||
traceroute_connectivity($host);
|
||||
|
|
Loading…
Reference in New Issue