#!/usr/bin/perl -w # # Copyright (C) 2005-2008 Gabriel Moreau. # # $Id: klask 69 2010-11-02 15:43:26Z g7moreau $ use strict; use warnings; use version; our $VERSION = qv('0.5.5'); use Readonly; use FileHandle; use Net::SNMP; #use YAML; use YAML::Syck; use Net::Netmask; use Net::CIDR::Lite; use NetAddr::IP; use Getopt::Long qw(GetOptions GetOptionsFromArray);; use Socket; # apt-get install snmp fping libnet-cidr-lite-perl libnet-netmask-perl libnet-snmp-perl libnetaddr-ip-perl libyaml-perl # libcrypt-des-perl libcrypt-hcesha-perl libdigest-hmac-perl # arping fping bind9-host arpwatch my $KLASK_VAR = '/var/cache/klask'; my $KLASK_CFG_FILE = '/etc/klask.conf'; my $KLASK_DB_FILE = "$KLASK_VAR/klaskdb"; my $KLASK_SW_FILE = "$KLASK_VAR/switchdb"; test_running_environnement(); my $KLASK_CFG = YAML::Syck::LoadFile("$KLASK_CFG_FILE"); my %DEFAULT = %{ $KLASK_CFG->{default} }; my @SWITCH = @{ $KLASK_CFG->{switch} }; my %switch_level = (); my %SWITCH_DB = (); LEVEL_OF_EACH_SWITCH: for my $sw (@SWITCH){ $switch_level{$sw->{hostname}} = $sw->{level} || $DEFAULT{switch_level} || 2; $SWITCH_DB{$sw->{hostname}} = $sw; } @SWITCH = reverse sort { $switch_level{$a->{hostname}} <=> $switch_level{$b->{hostname}} } @{$KLASK_CFG->{switch}}; my %SWITCH_PORT_COUNT = (); my %CMD_DB = ( help => \&cmd_help, version => \&cmd_version, exportdb => \&cmd_exportdb, updatedb => \&cmd_updatedb, searchdb => \&cmd_searchdb, removedb => \&cmd_removedb, search => \&cmd_search, enable => \&cmd_enable, disable => \&cmd_disable, status => \&cmd_status, updatesw => \&cmd_updatesw, exportsw => \&cmd_exportsw, iplocation => \&cmd_iplocation, 'ip-free' => \&cmd_ip_free, 'search-mac-on-switch' => \&cmd_search_mac_on_switch, ); Readonly my %INTERNAL_PORT_MAP => ( 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', 7 => 'H', ); Readonly my %INTERNAL_PORT_MAP_REV => reverse %INTERNAL_PORT_MAP; Readonly my %SWITCH_KIND => ( J3299A => { model => 'HP224M', match => 'HP J3299A ProCurve Switch 224M' }, J4120A => { model => 'HP1600M', match => 'HP J4120A ProCurve Switch 1600M' }, J9029A => { model => 'HP1800-8G', match => 'PROCURVE J9029A' }, J9449A => { model => 'HP1810-8G', match => 'HP ProCurve 1810G - 8 GE' }, J4093A => { model => 'HP2424M', match => 'HP J4093A ProCurve Switch 2424M' }, J4813A => { model => 'HP2524', match => 'HP J4813A ProCurve Switch 2524' }, J4900A => { model => 'HP2626A', match => 'HP J4900A ProCurve Switch 2626' }, J4900B => { model => 'HP2626B', match => 'J4900B.+?Switch 2626' },# ProCurve J4900B Switch 2626 # HP J4900B ProCurve Switch 2626 J4899B => { model => 'HP2650', match => 'ProCurve J4899B Switch 2650' }, J9021A => { model => 'HP2810-24G', match => 'ProCurve J9021A Switch 2810-24G' }, J9022A => { model => 'HP2810-48G', match => 'ProCurve J9022A Switch 2810-48G' }, J4903A => { model => 'HP2824', match => 'J4903A.+?Switch 2824,' }, J4110A => { model => 'HP8000M', match => 'HP J4110A ProCurve Switch 8000M' }, BS350T => { model => 'BS350T', match => 'BayStack 350T HW' }, ); Readonly my %OID_NUMBER => ( sysDescription => '1.3.6.1.2.1.1.1.0', sysName => '1.3.6.1.2.1.1.5.0', sysContact => '1.3.6.1.2.1.1.4.0', sysLocation => '1.3.6.1.2.1.1.6.0', ); Readonly my $RE_MAC_ADDRESS => qr{ [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} }xms; Readonly my $RE_IPv4_ADDRESS => qr{ [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} }xms; ################ # principal ################ my $cmd = shift @ARGV || 'help'; if (defined $CMD_DB{$cmd}) { $CMD_DB{$cmd}->(@ARGV); } else { print {*STDERR} "klask: command $cmd not found\n\n"; $CMD_DB{help}->(); exit 1; } exit; sub test_running_environnement { die "Configuration file $KLASK_CFG_FILE does not exists. Klask need it !\n" if not -e "$KLASK_CFG_FILE"; die "Var folder $KLASK_VAR does not exists. Klask need it !\n" if not -d "$KLASK_VAR"; return; } sub test_switchdb_environnement { die "Switch database $KLASK_SW_FILE does not exists. Launch updatesw before this command !\n" if not -e "$KLASK_SW_FILE"; return; } sub test_maindb_environnement { die "Main database $KLASK_DB_FILE does not exists. Launch updatedb before this command !\n" if not -e "$KLASK_DB_FILE"; return; } ### # fast ping dont l'objectif est de remplir la table arp de la machine sub fastping { # Launch this command without waiting... system "fping -c 1 @_ >/dev/null 2>&1 &"; return; } sub shell_command { my $cmd = shift; my $fh = new FileHandle; my $result = ''; open $fh, q{-|}, "$cmd" or die "Can't exec $cmd\n"; $result .= <$fh>; close $fh; chomp $result; return $result; } ### # donne l'@ ip, dns, arp en fonction du dns OU de l'ip sub resolve_ip_arp_host { my $param_ip_or_host = shift; my $interface = shift || q{*}; my $type = shift || q{fast}; my %ret = ( hostname_fq => 'unknow', ipv4_address => '0.0.0.0', mac_address => 'unknow', ); # my $cmdarping = `arping -c 1 -w 1 -rR $param 2>/dev/null`; # if ( not $param_ip_or_host =~ m/^\d+ \. \d+ \. \d+ \. \d+$/xms ) { # $param_ip_or_host =~ s/ \. .* //xms; # } #my $dns_resolver = Net::DNS::Resolver->new; #my $dns_answer = $dns_resolver->query($param_ip_or_host); #if ($dns_answer) { # $ret{ipv4_address} = $dns_answer->address; # } # perl -MSocket -E 'say inet_ntoa(scalar gethostbyname("tech7meylan.hmg.inpg.fr"))' my $packed_ip = scalar gethostbyname($param_ip_or_host); return %ret if not defined $packed_ip; $ret{ipv4_address} = inet_ntoa($packed_ip); # perl -MSocket -E 'say scalar gethostbyaddr(inet_aton("194.254.66.240"), AF_INET)' my $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET); # return %ret if not defined $hostname_fq; $ret{hostname_fq} = $hostname_fq if defined $hostname_fq; #print "DEBUG : $param_ip_or_host -- $ret{ipv4_address} -- $hostname_fq \n"; # controler que arpwatch tourne ! # resultat de la commande arpwatch # /var/lib/arpwatch/arp.dat # 0:13:d3:e1:92:d0 192.168.24.109 1163681980 theo8sv109 # my $cmd = "grep -e '".'\b'."$param_ip_or_host".'\b'."' /var/lib/arpwatch/arp.dat | sort +2rn | head -1"; # my $cmd = "grep -he '".'\b'."$param_ip_or_host".'\b'."' /var/lib/arpwatch/*.dat | sort +2rn | head -1"; ### BOGUE ICI ### FAIRE LE GREP SUR L'IP ! # my $cmd = q{grep -he '\b} . $param_ip_or_host . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1"; my $cmd = q{grep -he '\b} . $ret{ipv4_address} . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1"; my $cmd_arpwatch = shell_command $cmd; my ($arp, $ip, $timestamp, $host) = split m/ \s+ /xms, $cmd_arpwatch; # $ret{ipv4_address} = $ip if $ip; $ret{mac_address} = $arp if $arp; $ret{timestamp} = $timestamp if $timestamp; my $nowtimestamp = time; if ( $type eq 'fast' and ( not defined $timestamp or $timestamp < ( $nowtimestamp - 3 * 3600 ) ) ) { $ret{mac_address} = 'unknow'; return %ret; } # resultat de la commande arp # tech7meylan.hmg.inpg.fr (194.254.66.240) at 00:14:22:45:28:A9 [ether] on eth0 # sw2-batF0-legi.hmg.priv (192.168.22.112) at 00:30:c1:76:9c:01 [ether] on eth0.37 my $cmd_arp = shell_command "arp -a $param_ip_or_host"; if ( $cmd_arp =~ m{ (\S*) \s \( ( $RE_IPv4_ADDRESS ) \) \s at \s ( $RE_MAC_ADDRESS ) }xms ) { ( $ret{hostname_fq}, $ret{ipv4_address}, $ret{mac_address} ) = ($1, $2, $3); } # resultat de la commande host si le parametre est ip # 250.66.254.194.in-addr.arpa domain name pointer legihp2100.hmg.inpg.fr. # my $cmd_host = shell_command "host $param_ip_or_host"; # if ( $cmd_host =~ m/domain \s name \s pointer \s (\S+) \.$/xms ) { # $ret{hostname_fq} = $1; # } # resultat de la commande host si parametre est hostname # tech7meylan.hmg.inpg.fr has address 194.254.66.240 # if ( $cmd_host =~ m/(\S*) \s has \s address \s ( $RE_IPv4_ADDRESS )$/xms ) { # ( $ret{hostname_fq}, $ret{ipv4_address} ) = ($1, $2); # } # Connerie ! # Inverse les IP !! # if ( $cmd_host =~ m/ \b ( $RE_IPv4_ADDRESS ) \. in-addr \. arpa \s/xms ) { # $ret{ipv4_address} = $1; # } #$ret{hostname_fq} = $param_ip_or_host if not defined $1 and $ret{hostname_fq} eq 'unknow'; if ($ret{mac_address} ne 'unknow') { my @paquets = (); foreach ( split m/ : /xms, $ret{mac_address} ) { my @chars = split m//xms, uc "00$_"; push @paquets, "$chars[-2]$chars[-1]"; } $ret{mac_address} = join q{:}, @paquets; } return %ret; } # Find Surname of a switch sub get_switch_model { my $sw_snmp_description = shift || 'unknow'; for my $sw_kind (keys %SWITCH_KIND) { next if not $sw_snmp_description =~ m/$SWITCH_KIND{$sw_kind}->{match}/ms; # option xms break search, why ? return $SWITCH_KIND{$sw_kind}->{model}; } return $sw_snmp_description; } ### # va rechercher le nom des switchs pour savoir qui est qui sub init_switch_names { my $verbose = shift; printf "%-25s %-25s %s\n",'Switch','Description','Type'; print "-------------------------------------------------------------------------\n" if $verbose; INIT_EACH_SWITCH: for my $sw (@SWITCH) { my %session = ( -hostname => $sw->{hostname} ); $session{-version} = $sw->{version} || 1; $session{-port} = $sw->{snmpport} || $DEFAULT{snmpport} || 161; if (exists $sw->{version} and $sw->{version} eq '3') { $session{-username} = $sw->{username} || 'snmpadmin'; } else { $session{-community} = $sw->{community} || $DEFAULT{community} || 'public'; } $sw->{local_session} = \%session; my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} ); print "$error \n" if $error; my $result = $session->get_request( -varbindlist => [ $OID_NUMBER{sysDescription}, $OID_NUMBER{sysName}, $OID_NUMBER{sysContact}, $OID_NUMBER{sysLocation}, ] ); $sw->{description} = $result->{$OID_NUMBER{sysName}} || $sw->{hostname}; $sw->{model} = get_switch_model( $result->{$OID_NUMBER{sysDescription}}); #$sw->{location} = $result->{"1.3.6.1.2.1.1.6.0"} || $sw->{hostname}; #$sw->{contact} = $result->{"1.3.6.1.2.1.1.4.0"} || $sw->{hostname}; $session->close; # Ligne à virer car on récupère maintenant le modèle du switch my ($desc, $type) = split m/ : /xms, $sw->{description}, 2; printf "%-25s 0--------->>>> %-25s %s\n", $sw->{hostname}, $desc, $sw->{model} if $verbose; } print "\n" if $verbose; return; } ### # convertit l'hexa (uniquement 2 chiffres) en decimal sub hex_to_dec { #00:0F:1F:43:E4:2B my $car = '00' . uc shift; return '00' if $car eq '00UNKNOW'; my %table = ( '0'=>'0', '1'=>'1', '2'=>'2', '3'=>'3', '4'=>'4', '5'=>'5', '6'=>'6', '7'=>'7', '8'=>'8', '9'=>'9', 'A'=>'10', 'B'=>'11', 'C'=>'12', 'D'=>'13', 'E'=>'14', 'F'=>'15', ); my @chars = split m//xms, $car; return $table{$chars[-2]}*16 + $table{$chars[-1]}; } ### # convertit l'@ arp en decimal sub arp_hex_to_dec { #00:0F:1F:43:E4:2B my $arp = shift; my @paquets = split m/ : /xms, $arp; my $return = q{}; foreach(@paquets) { $return .= q{.} . hex_to_dec($_); } return $return; } ### # va rechercher le port et le switch sur lequel est la machine sub find_switch_port { my $arp = shift; my $switch_proposal = shift || q{}; my %ret; $ret{switch_description} = 'unknow'; $ret{switch_port} = '0'; return %ret if $arp eq 'unknow';; my @switch_search = @SWITCH; if ($switch_proposal ne q{}) { for my $sw (@SWITCH) { next if $sw->{hostname} ne $switch_proposal; unshift @switch_search, $sw; last; } } my $research = '1.3.6.1.2.1.17.4.3.1.2' . arp_hex_to_dec($arp); LOOP_ON_SWITCH: for my $sw (@switch_search) { my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} ); print "$error \n" if $error; my $result = $session->get_request( -varbindlist => [$research] ); if (not defined $result or $result->{$research} eq 'noSuchInstance') { $session->close; next LOOP_ON_SWITCH; } my $swport = $result->{$research}; $session->close; # IMPORTANT !! # ceci empeche la detection sur certains port ... # en effet les switch sont relies entre eux par un cable reseau et du coup # tous les arp de toutes les machines sont presentes sur ces ports (ceux choisis ici sont les miens) # cette partie est a ameliore, voir a configurer dans l'entete # 21->24 45->48 # my $flag = 0; SWITCH_PORT_IGNORE: foreach my $p (@{$sw->{portignore}}) { next SWITCH_PORT_IGNORE if $swport ne get_numerical_port($sw->{model},$p); # $flag = 1; next LOOP_ON_SWITCH; } # if ($flag == 0) { $ret{switch_hostname} = $sw->{hostname}; $ret{switch_description} = $sw->{description}; $ret{switch_port} = get_human_readable_port($sw->{model}, $swport); # $swport; last LOOP_ON_SWITCH; # } # } # $session->close; } return %ret; } ### # va rechercher les port et les switch sur lequel est la machine sub find_all_switch_port { my $arp = shift; my $ret = {}; return $ret if $arp eq 'unknow'; for my $sw (@SWITCH) { $SWITCH_PORT_COUNT{$sw->{hostname}} = {} if not exists $SWITCH_PORT_COUNT{$sw->{hostname}}; } my $research = '1.3.6.1.2.1.17.4.3.1.2' . arp_hex_to_dec($arp); LOOP_ON_ALL_SWITCH: for my $sw (@SWITCH) { my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} ); print "$error \n" if $error; my $result = $session->get_request( -varbindlist => [$research] ); if(defined $result and $result->{$research} ne 'noSuchInstance'){ my $swport = $result->{$research}; #print "DEBUG $arp $swport -- $sw->{hostname} \n"; if ( $sw->{hostname} eq 'sw10-batE1-3s.hmg.priv' and $swport == 19 ) { $swport = 20; print "DEBUG $swport -- $sw->{hostname} \n";} if ( $sw->{hostname} eq 'sw8-batE1-3s.hmg.priv' and $swport == 23 ) { $swport = 24; print "DEBUG $swport -- $sw->{hostname} \n";} # if ( $sw->{hostname} eq 'sw10-batE1-3s.hmg.priv' ) { print "DEBUG $swport -- $sw->{hostname} \n";} # if ( $sw->{hostname} eq 'sw8-batE1-3s.hmg.priv' ) { print "DEBUG $swport -- $sw->{hostname} \n";} $ret->{$sw->{hostname}} = {}; $ret->{$sw->{hostname}}{hostname} = $sw->{hostname}; $ret->{$sw->{hostname}}{description} = $sw->{description}; $ret->{$sw->{hostname}}{port} = get_human_readable_port($sw->{model}, $swport); $SWITCH_PORT_COUNT{$sw->{hostname}}->{$swport}++; } $session->close; } return $ret; } sub get_list_network { return keys %{$KLASK_CFG->{network}}; } sub get_current_interface { my $network = shift; return $KLASK_CFG->{network}{$network}{interface}; } ### # liste l'ensemble des adresses ip d'un réseau sub get_list_ip { my @network = @_; my $cidrlist = Net::CIDR::Lite->new; for my $net (@network) { my @line = @{$KLASK_CFG->{network}{$net}{'ip-subnet'}}; for my $cmd (@line) { for my $method (keys %{$cmd}){ $cidrlist->add_any($cmd->{$method}) if $method eq 'add'; } } } my @res = (); for my $cidr ($cidrlist->list()) { my $net = new NetAddr::IP $cidr; for my $ip (@{$net}) { $ip =~ s{ /32 }{}xms; push @res, $ip; } } return @res; } # liste l'ensemble des routeurs du réseau sub get_list_main_router { my @network = @_; my @res = (); for my $net (@network) { push @res, $KLASK_CFG->{network}{$net}{'main-router'}; } return @res; } sub get_human_readable_port { my $sw_model = shift; my $sw_port = shift; if ($sw_model eq 'HP8000M') { my $reste = (($sw_port - 1) % 8) + 1; my $major = int (($sw_port - 1) / 8); return "$INTERNAL_PORT_MAP{$major}$reste"; } if ($sw_model eq 'HP2424M') { if ($sw_port > 24) { my $reste = $sw_port - 24; return "A$reste"; } } if ($sw_model eq 'HP1600M') { if ($sw_port > 16) { my $reste = $sw_port - 16; return "A$reste"; } } return $sw_port; } sub get_numerical_port { my $sw_model = shift; my $sw_port = shift; if ($sw_model eq 'HP8000M') { my $letter = substr $sw_port, 0, 1; my $reste = substr $sw_port, 1; return $INTERNAL_PORT_MAP_REV{$letter} * 8 + $reste; } if ($sw_model eq 'HP2424M') { if ($sw_port =~ m/^A/xms ) { my $reste = substr $sw_port, 1; return 24 + $reste; } } if ($sw_model eq 'HP1600M') { if ($sw_port =~ m/^A/xms ) { my $reste = substr $sw_port, 1; return 16 + $reste; } } return $sw_port; } ################ # Les commandes ################ sub cmd_help { print <<'END'; klask - ports manager and finder for switch klask updatedb klask exportdb --format [txt|html] klask updatesw klask exportsw --format [txt|dot] klask searchdb computer klask search computer klask search-mac-on-switch switch mac_addr klask ip-free --day number_of_day [vlan_name] klask enable switch port klask disable switch port klask status switch port END return; } sub cmd_version { print <<'END'; Klask - ports manager and finder for switch Copyright (C) 2005-2008 Gabriel Moreau END print ' $Rev: 69 $'."\n"; print ' $Date: 2010-11-02 15:43:26 +0000 (Tue, 02 Nov 2010) $'."\n"; print ' $Id: klask 69 2010-11-02 15:43:26Z g7moreau $'."\n"; return; } sub cmd_search { my @computer = @_; init_switch_names(); #nomme les switchs fastping(@computer); for my $clientname (@computer) { my %resol_arp = resolve_ip_arp_host($clientname); #resolution arp my %where = find_switch_port($resol_arp{mac_address}); #retrouve l'emplacement printf '%-22s %2i %-30s %-15s %18s', $where{switch_description}, $where{switch_port}, $resol_arp{hostname_fq}, $resol_arp{ipv4_address}, $resol_arp{mac_address}."\n" unless $where{switch_description} eq 'unknow' and $resol_arp{hostname_fq} eq 'unknow' and $resol_arp{mac_address} eq 'unknow'; } return; } sub cmd_searchdb { my @computer = @_; fastping(@computer); my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE"); LOOP_ON_COMPUTER: for my $clientname (@computer) { my %resol_arp = resolve_ip_arp_host($clientname); #resolution arp my $ip = $resol_arp{ipv4_address}; next LOOP_ON_COMPUTER unless exists $computerdb->{$ip}; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp}; $year += 1900; $mon++; my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min; printf "%-22s %2s %-30s %-15s %-18s %s\n", $computerdb->{$ip}{switch_name}, $computerdb->{$ip}{switch_port}, $computerdb->{$ip}{hostname_fq}, $ip, $computerdb->{$ip}{mac_address}, $date; } return; } sub cmd_updatedb { my @network = @_; @network = get_list_network() if not @network; test_switchdb_environnement(); my $computerdb = {}; $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE"; my $timestamp = time; my %computer_not_detected = (); my $timestamp_last_week = $timestamp - (3600 * 24 * 7); my $number_of_computer = get_list_ip(@network); # + 1; my $size_of_database = keys %{$computerdb}; $size_of_database = 1 if $size_of_database == 0; my $i = 0; my $detected_computer = 0; init_switch_names('yes'); #nomme les switchs { # Remplis le champs portignore des ports d'inter-connection pour chaque switch my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE"); my %db_switch_output_port = %{$switch_connection->{output_port}}; my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}}; my %db_switch_chained_port = (); for my $swport (keys %db_switch_connected_on_port) { my ($sw_connect,$port_connect) = split m/ : /xms, $swport; $db_switch_chained_port{$sw_connect} .= "$port_connect:"; } for my $sw (@SWITCH){ push @{$sw->{portignore}}, $db_switch_output_port{$sw->{hostname}} if exists $db_switch_output_port{$sw->{hostname}}; if ( exists $db_switch_chained_port{$sw->{hostname}} ) { chop $db_switch_chained_port{$sw->{hostname}}; push @{$sw->{portignore}}, split m/ : /xms, $db_switch_chained_port{$sw->{hostname}}; } # print "$sw->{hostname} ++ @{$sw->{portignore}}\n"; } } my %router_mac_ip = (); DETECT_ALL_ROUTER: # for my $one_router ('194.254.66.254') { for my $one_router ( get_list_main_router(@network) ) { my %resol_arp = resolve_ip_arp_host($one_router); $router_mac_ip{ $resol_arp{mac_address} } = $resol_arp{ipv4_address}; } ALL_NETWORK: for my $net (@network) { my @computer = get_list_ip($net); my $current_interface = get_current_interface($net); fastping(@computer); LOOP_ON_COMPUTER: for my $one_computer (@computer) { $i++; my $total_percent = int (($i*100)/$number_of_computer); my $localtime = time - $timestamp; my ($sec,$min) = localtime $localtime; my $time_elapse = 0; $time_elapse = $localtime * ( 100 - $total_percent) / $total_percent if $total_percent != 0; my ($sec_elapse,$min_elapse) = localtime $time_elapse; printf "\rComputer scanned: %4i/%i (%2i%%)", $i, $number_of_computer, $total_percent; # printf ", Computer detected: %4i/%i (%2i%%)", $detected_computer, $size_of_database, int(($detected_computer*100)/$size_of_database); printf ', detected: %4i/%i (%2i%%)', $detected_computer, $size_of_database, int(($detected_computer*100)/$size_of_database); printf ' [Time: %02i:%02i / %02i:%02i]', int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60; # printf ' [%02i:%02i/%02i:%02i]', int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60; printf ' %-14s', $one_computer; my %resol_arp = resolve_ip_arp_host($one_computer,$current_interface); # do not search on router connection (why ?) if ( exists $router_mac_ip{$resol_arp{mac_address}}) { $computer_not_detected{$one_computer} = $current_interface; next LOOP_ON_COMPUTER; } # do not search on switch inter-connection if (exists $switch_level{$resol_arp{hostname_fq}}) { $computer_not_detected{$one_computer} = $current_interface; next LOOP_ON_COMPUTER; } my $switch_proposal = q{}; if (exists $computerdb->{$resol_arp{ipv4_address}} and exists $computerdb->{$resol_arp{ipv4_address}}{switch_hostname}) { $switch_proposal = $computerdb->{$resol_arp{ipv4_address}}{switch_hostname}; } # do not have a mac address if ($resol_arp{mac_address} eq 'unknow' or (exists $resol_arp{timestamps} and $resol_arp{timestamps} < ($timestamp - 3 * 3600))) { $computer_not_detected{$one_computer} = $current_interface; next LOOP_ON_COMPUTER; } my %where = find_switch_port($resol_arp{mac_address},$switch_proposal); #192.168.24.156: # arp: 00:0B:DB:D5:F6:65 # hostname: pcroyon.hmg.priv # port: 5 # switch: sw-batH-legi:hp2524 # timestamp: 1164355525 # do not have a mac address # if ($resol_arp{mac_address} eq 'unknow') { # $computer_not_detected{$one_computer} = $current_interface; # next LOOP_ON_COMPUTER; # } # detected on a switch if ($where{switch_description} ne 'unknow') { $detected_computer++; $computerdb->{$resol_arp{ipv4_address}} = { hostname_fq => $resol_arp{hostname_fq}, mac_address => $resol_arp{mac_address}, switch_hostname => $where{switch_hostname}, switch_description => $where{switch_description}, switch_port => $where{switch_port}, timestamp => $timestamp, network => $net, }; next LOOP_ON_COMPUTER; } # new in the database but where it is ? if (not exists $computerdb->{$resol_arp{ipv4_address}}) { $detected_computer++; $computerdb->{$resol_arp{ipv4_address}} = { hostname_fq => $resol_arp{hostname_fq}, mac_address => $resol_arp{mac_address}, switch_hostname => $where{switch_hostname}, switch_description => $where{switch_description}, switch_port => $where{switch_port}, timestamp => $resol_arp{timestamp}, network => $net, }; } # mise a jour du nom de la machine si modification dans le dns $computerdb->{$resol_arp{ipv4_address}}{hostname_fq} = $resol_arp{hostname_fq}; # mise à jour de la date de détection si détection plus récente par arpwatch $computerdb->{$resol_arp{ipv4_address}}{timestamp} = $resol_arp{timestamp} if exists $resol_arp{timestamp} and $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $resol_arp{timestamp}; # relance un arping sur la machine si celle-ci n'a pas été détectée depuis plus d'une semaine # push @computer_not_detected, $resol_arp{ipv4_address} if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week; $computer_not_detected{$resol_arp{ipv4_address}} = $current_interface if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week; } } # final end of line at the end of the loop printf "\n"; my $dirdb = $KLASK_DB_FILE; $dirdb =~ s{ / [^/]* $}{}xms; mkdir "$dirdb", 0755 unless -d "$dirdb"; YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb); for my $one_computer (keys %computer_not_detected) { my $interface = $computer_not_detected{$one_computer}; system "arping -c 1 -w 1 -rR -i $interface $one_computer &>/dev/null"; # print "arping -c 1 -w 1 -rR -i $interface $one_computer 2>/dev/null\n"; } return; } sub cmd_removedb { my @computer = @_; test_maindb_environnement(); my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE"); LOOP_ON_COMPUTER: for my $one_computer (@computer) { if ( $one_computer =~ m/^ $RE_IPv4_ADDRESS $/xms and exists $computerdb->{$one_computer} ) { delete $computerdb->{$one_computer}; next; } my %resol_arp = resolve_ip_arp_host($one_computer); delete $computerdb->{$resol_arp{ipv4_address}} if exists $computerdb->{$resol_arp{ipv4_address}}; } my $dirdb = $KLASK_DB_FILE; $dirdb =~ s{ / [^/]* $}{}xms; mkdir "$dirdb", 0755 unless -d "$dirdb"; YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb); return; } sub cmd_exportdb { my @ARGV = @_; my $format = 'txt'; my $ret = GetOptions( 'format|f=s' => \$format, ); my %possible_format = ( txt => \&cmd_exportdb_txt, html => \&cmd_exportdb_html, ); $format = 'txt' if not defined $possible_format{$format}; $possible_format{$format}->(@ARGV); return; } sub cmd_exportdb_txt { test_maindb_environnement(); my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE"); printf "%-24s %-4s %-30s %-15s %-18s %-s\n", qw(Switch Port Hostname IPv4-Address MAC-Address Date); print "---------------------------------------------------------------------------------------------------------------------------\n"; LOOP_ON_IP_ADDRESS: foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) { # next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq 'unknow'; # to be improve in the future next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself ! # dans le futur # next if $computerdb->{$ip}{hostname_fq} eq 'unknow'; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp}; $year += 1900; $mon++; my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min; printf "%-25s %2s <------- %-30s %-15s %-18s %s\n", $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}, $computerdb->{$ip}{switch_port}, $computerdb->{$ip}{hostname_fq}, $ip, $computerdb->{$ip}{mac_address}, $date; } return; } sub cmd_exportdb_html { test_maindb_environnement(); my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE"); # # print <<'END_HTML';
Switch | Port | Link | Hostname | IPv4-Address | MAC-Address | Date |
---|---|---|---|---|---|---|
Switch | Port | Link | Hostname | IPv4-Address | MAC-Address | Date |
$switch_hostname | $computerdb->{$ip}{switch_port} | <------- | $computerdb->{$ip}{hostname_fq} | $ip | $computerdb->{$ip}{mac_address} | $date |
$sw | $db_switch_output_port{$sw}> | +--> $db_switch_parent{$sw}->{port} | $db_switch_parent{$sw}->{switch}> | $ipv4_address | $mac_address | $date |
$sw | $db_switch_output_port{$sw}> | +--> | router> | |||
$sw_connect | $port_connect> | <--+ $db_switch_output_port{$sw} | $sw> | $ipv4_address | $mac_address | $date |
$sw_connect | $port_connect> | <--+ | $sw> | $ipv4_address | $mac_address | $date |