#!/usr/bin/perl -w use strict; use IP; #snmpwalk flags reference (full version man snmpwalkcmd # #-Oq Removes the equal sign and type information: #-Os Deletes all but the last symbolic part of the OID: #-Ov Output only the variable value, not the OID: #-O0 Print leading zero before HEX data #id is het ip #0 = mac #1 = subnet #2 = member of node #3 = icmp_active (0 down, 1 active, 9 not checked, 10 not passed (yet)) #4 = snmp_active (0 down, 1 active, 10 not passed(yet)) #host to start walk my $start_host= '172.16.1.165'; #debug my $debug_enable = 0; my $verbose_enable = 1; #disallowed ip's my %denied_ip_list = (); $denied_ip_list{'127.0.0.1'} = 1; #localhost $denied_ip_list{'172.31.255.1'} =1; #general proxy #disallowed mac's my %denied_mac_list = (); $denied_mac_list{'ff:ff:ff:ff:ff:ff'} = 1; #ip's addr not active #tuning my $snmp_timeout = "0.5"; #seconds my $icmp_timeout = "1"; #seconds my $icmp_retry = "1"; #times #dictionary variable my $unknown_node = "unknown_node"; my $remote_node = "remote_node"; my $nodes_file = "nodes.txt"; my $result_file = "result.txt"; ##############################END OF CONFIG############################ my %ipDB = (); my %online_nodes = (); #regular expressions my $ipv4_regexp = '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})'; my $mac_regexp = '[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}'; #tmp variablen my $raw_string = ""; my @raw_array = (); my %raw_hash = (); my $nodename = ""; #File's to use open NODES, "> $nodes_file" or die("Cann't open $nodes_file : $!"); open RESULT, "> $result_file" or die("Cann't open $result_file : $!"); #debugging sub sub debug { if( $debug_enable ) { print "$_[0] \n"; } }; #verbose sub sub verbose { if( $verbose_enable or $debug_enable ) { print "$_[0] \n"; } }; #align files nice sub align { my $align = "/usr/local/bin/align"; if( -e $align ) { my $file = $_[0]; system("$align -s s+ $file > $file.tmp"); system("mv $file.tmp $file"); } else { verbose("$align doesn't seems to exists"); }; }; #close files after use sub close_files { close NODES; close RESULT; align($nodes_file); align($result_file); }; #run snmp_command; sub snmp_command { my $flags = $_[0]; my $host = $_[1]; my $identifier = $_[2]; debug("host = $host"); my $command = "snmpwalk -t$snmp_timeout -v 1 -O$flags -c public $host $identifier"; debug("command =$command"); my $raw_output = `$command`; debug("Raw output = $raw_output"); $raw_output =~ s/\n$//; debug("Reniced output = $raw_output"); return($raw_output); }; # ip's who are used for special purposes like, loopback or general # aliasss are not allowed to use # some mac addr are not allowed either # if no mac address is found don't allow either sub allowed_ip { my $ip = $_[0]; debug("checking whether $ip is allowed"); if( exists $denied_ip_list{$ip} ) { debug("$ip is at the 'ip denied list'"); return(0); } elsif( defined $ipDB{$ip}[0] and exists $denied_mac_list{$ipDB{$ip}[0]} ) { debug("$ip with $ipDB{$ip}[0] is at the 'mac denied list'"); return(0); } else { debug("no problem at all"); return(1); }; }; #during this part there will be checked whether we could use a ip #address as neighboor ip or as local ip #This is written to enimilate the acess point's ip of the clients sub ip_remote_check { no strict 'refs'; my $ip = $_[0]; my $tmpSubnet_ref = $_[1]; my $local_ips_ref = $_[2]; my $ip2subnet_ref = $_[3]; foreach my $local_ip (keys %$local_ips_ref) { debug("checking if $ip is at $local_ip / $$ip2subnet_ref{$local_ip}"); if( IP::calcOnNetwork($local_ip, $ip, $$ip2subnet_ref{$local_ip}) ) { $$tmpSubnet_ref = $$ip2subnet_ref{$local_ip}; debug("$ip has subnet $$tmpSubnet_ref"); if( IP::getNetmask($$ip2subnet_ref{$local_ip}) > 28 ) { debug("$ip seems to be really remote of $local_ip"); return(1); }; }; }; return(0); }; #zeer grote sub, die de data uit een node plukt de nodige mutaties doet #en ervoor zorgt dat er te weten wordt gekomen welke ip gegevens er #actief zijn sub node_check { my $host = $_[0]; my $remote_ips_ref = $_[1]; my $tmpSubnet = ""; my $tmpIP = ""; #some hashes my %local_ips = (); my %ip2mac = (); my %ip2subnet = (); #creeren van een hash om de subnetten op te zoeken #ipAdEntNetMask.172.16.1.166 255.255.255.252 -> #$ip2subnetPart{172.16.1.166} = 255.255.255.252 $raw_string = snmp_command("qs",$host,"ipAdEntNetMask"); @raw_array = split(/\n/, $raw_string); foreach my $line (@raw_array) { if( $line =~ /\S*\.($ipv4_regexp) ($ipv4_regexp)$/ ) { debug("ip $1 at $6"); if( not exists $ip2subnet{$1} ) { $ip2subnet{$1} = $6; }; }; }; #pluis de physieke (met een mac verwijzing) ip addressen uit #ipNetToMediaPhysAddress.4.172.27.129.1 00:02:6f:34:3a:ed -> #$ipDB{172.27.129.1}[0] = 00:02:6f:34:3a:ed $raw_string = snmp_command("0qs", $host, "ipNetToMediaPhysAddress"); @raw_array = split(/\n/, $raw_string); foreach my $line (@raw_array) { if( $line =~ /\S*\.($ipv4_regexp) ($mac_regexp)/ ) { if( not exists $ipDB{$1} ) { $ipDB{$1}[0] = $6; if( exists $ip2subnet{$1} ) { $ipDB{$1}[1] = $ip2subnet{$1}; } else { $ipDB{$1}[1] = "subnet_unknown" }; $ipDB{$1}[2] = $unknown_node; $ipDB{$1}[3] = 10; $ipDB{$1}[4] = 10; debug("ip $1 has $6 at $ipDB{$1}[1]"); } else { if( not defined $ipDB{$1}[0] ) { $ipDB{$1}[0] = $6; }; if( not defined $ipDB{$1}[1] ) { $ipDB{$1}[1] = $ip2subnet{$1}; }; }; }; }; #ipAdEntIfIndex.127.0.0.1 3 #$ipDB{'127.0.0.1'}[2] = 'nodename' #$local_ips{'127.0.0.1'} = subnet node $raw_string = `snmpwalk -v 1 -Osq -c public $host ipAdEntIfIndex`; @raw_array = split(/\n/, $raw_string); foreach my $line (@raw_array) { if( $line =~ /\S*\.($ipv4_regexp) (\d+)/ ) { $tmpIP = $1; if( allowed_ip($tmpIP) ) { debug("local $tmpIP is member of $nodename"); if( not defined $ipDB{$tmpIP}[0] ) { debug("local $tmpIP has no entry in 'ipDB yet"); $ipDB{$tmpIP}[0] = "unknown_mac"; if( exists $ip2subnet{$tmpIP} ) { $ipDB{$tmpIP}[1] = $ip2subnet{$tmpIP}; } else { $ipDB{$tmpIP}[1] = "subnet_unknown" }; $ipDB{$tmpIP}[3] = 10; $ipDB{$tmpIP}[4] = 10; }; $ipDB{$tmpIP}[2] = $nodename; $local_ips{$tmpIP} = $ip2subnet{$tmpIP}; }; }; }; #atIfIndex.1.1.172.27.129.66 1 #als ip niet in local_ips voorkomt dan #is het een remote ip en moet ie getest worden $raw_string = snmp_command("qs", $host, "atIfIndex"); @raw_array = split(/\n/, $raw_string); foreach my $line (@raw_array) { if( $line =~ /\S*\.($ipv4_regexp) \S*/ ) { if( allowed_ip($1) ) { if( $ipDB{$1}[2] eq $unknown_node ) { debug("remote ip $1"); if( ip_remote_check($1, \$tmpSubnet, \%local_ips, \%ip2subnet) ) { debug("remote ip $1 has probely node attached"); $ipDB{$1}[1] = $tmpSubnet; $ipDB{$1}[2] = $remote_node; $$remote_ips_ref{$1} = $ipDB{$1}[0]; } else { debug("remote ip $1 seems to be a exception, but has subnet $tmpSubnet"); $ipDB{$1}[1] = $tmpSubnet; $ipDB{$1}[2] = "exception" }; }; }; }; }; verbose("local ip's \n" . join( "\n", keys %local_ips ) ); verbose("\n\nremote ip's \n" . join( "\n", keys %$remote_ips_ref ) ); }; # Controle of het ip antwoord op de snmp oproep #en gelijk wat status info wegschrijven in de DB sub check_snmp_active { my $host = $_[0]; if( $nodename = snmp_command("sqv", $host, "sysName") ) { $ipDB{$host}[4]=1; debug("$host is active sysName $nodename"); $online_nodes{$nodename} = 1; print NODES "$host $nodename\n"; return(1); } else { $ipDB{$host}[4]=0; debug("$host seems not to respond"); return(0); }; }; # Controle of ip addr te 'pingen' is # en gelijk wat status informatie wegschrijven in de DB sub check_icmp_active { my $host = $_[0]; my $exit_code = system("ping -o -c$icmp_retry -t$icmp_timeout -n $host > /dev/null"); if( $exit_code == 0 ) { $ipDB{$host}[3] = 1; debug("$host pingable"); return(1); } else { $ipDB{$host}[3] = 0; debug("$host unreachable"); return(0); }; }; #kijk of een ip nog een bekend is op de stapel en of de node nog niet #bezocht is en voeg dan toe. sub add_to_stack { my $remote_ips_ref = $_[0]; my $stack_ref = $_[1]; my $stackDB_ref = $_[2]; foreach my $ip (keys %$remote_ips_ref) { if( not exists $$stack_ref{$ip} ) { if( not exists $online_nodes{$ipDB{$ip}[2]} ) { $$stack_ref{$ip} = 1; push(@$stackDB_ref, $ip); }; }; }; }; #ga dmv van een stack implementatie door het netwerk lopen #1. kijk of een ip te bereiken is #2. datamining van alle ip's gegevens. #3. toevoegen van 'remote ip's aan de stapel' #4. onderste van de stapel pakken en verder gaan. sub walkthrough { my %stack = (); my @stackDB = (); my %tmpRemote_ips = (); my $next_node = ""; if( check_icmp_active($start_host) ) { if( check_snmp_active($start_host) ) { node_check($start_host,\%tmpRemote_ips); add_to_stack(\%tmpRemote_ips, \%stack, \@stackDB); } else { verbose("$start_host doesn't seems to respond"); return(); }; } else { verbose("$start_host doesn't seems to respond"); return(); }; # # uncomment this to use ony one node # #if( $debug_enable ) { # return(); #}; # while( scalar(@stackDB) != 0 ) { $next_node = shift(@stackDB); verbose("\n--------------------------$next_node-------------------"); verbose("checking $next_node..."); if( check_icmp_active($next_node) ) { verbose("...icmp active"); if( not exists $online_nodes{$ipDB{$next_node}[2]} ) { verbose("...has maybe a non discovered node"); if( check_snmp_active($next_node) ) { verbose("...snmp active"); %tmpRemote_ips = (); node_check($next_node, \%tmpRemote_ips); add_to_stack(\%tmpRemote_ips, \%stack, \@stackDB); }; }; }; verbose("-----------------END of $next_node---------------------\n"); }; }; #schrijf het resultaat van het harde werken cq data verzamelen weg in #een file sub write_result { foreach my $ip (keys %ipDB) { print RESULT "$ip\t"; for my $tmpNumber (0 .. 4) { print RESULT "$ipDB{$ip}[$tmpNumber] "; }; print RESULT "\n"; }; }; walkthrough($start_host); write_result(); close_files();