source: trunk/klask @ 202

Last change on this file since 202 was 202, checked in by g7moreau, 7 years ago
  • Same date format y-m-d !
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Id Rev
File size: 91.4 KB
RevLine 
[2]1#!/usr/bin/perl -w
[32]2#
[195]3# Copyright (C) 2005-2017 Gabriel Moreau
[32]4#
5# $Id: klask 202 2017-01-29 15:27:43Z g7moreau $
[2]6
7use strict;
[3]8use warnings;
[200]9use version; our $VERSION = qv('0.5.9');
[2]10
[63]11use Readonly;
[64]12use FileHandle;
[2]13use Net::SNMP;
[44]14#use YAML;
15use YAML::Syck;
[2]16use Net::Netmask;
17use Net::CIDR::Lite;
18use NetAddr::IP;
[110]19use Getopt::Long qw(GetOptions);
[68]20use Socket;
[109]21use List::Util 'shuffle';
[2]22
[8]23# apt-get install snmp fping libnet-cidr-lite-perl libnet-netmask-perl libnet-snmp-perl libnetaddr-ip-perl libyaml-perl
[198]24# libcrypt-des-perl libcrypt-hcesha-perl libdigest-hmac-perl
[79]25# arping net-tools fping bind9-host arpwatch
[2]26
[100]27my $KLASK_VAR      = '/var/lib/klask';
[92]28my $KLASK_CFG_FILE = '/etc/klask/klask.conf';
[28]29my $KLASK_DB_FILE  = "$KLASK_VAR/klaskdb";
30my $KLASK_SW_FILE  = "$KLASK_VAR/switchdb";
[2]31
[26]32test_running_environnement();
33
[44]34my $KLASK_CFG = YAML::Syck::LoadFile("$KLASK_CFG_FILE");
[2]35
[196]36my %DEFAULT     = %{$KLASK_CFG->{default}};
37my @SWITCH_LIST = @{$KLASK_CFG->{switch}};
[2]38
[196]39my %SWITCH_LEVEL = ();
[63]40my %SWITCH_DB    = ();
[2]41LEVEL_OF_EACH_SWITCH:
[196]42for my $sw (@SWITCH_LIST){
43   $SWITCH_LEVEL{$sw->{hostname}} = $sw->{level} || $DEFAULT{switch_level}  || 2;
[25]44   $SWITCH_DB{$sw->{hostname}} = $sw;
[2]45   }
[196]46@SWITCH_LIST = reverse sort { $SWITCH_LEVEL{$a->{hostname}} <=> $SWITCH_LEVEL{$b->{hostname}} } @{$KLASK_CFG->{switch}};
[2]47
[196]48#my %SWITCH_PORT_COUNT = ();
[2]49
[11]50my %CMD_DB = (
[133]51   'help'                 => \&cmd_help,
52   'version'              => \&cmd_version,
53   'exportdb'             => \&cmd_exportdb,
54   'updatedb'             => \&cmd_updatedb,
55   'searchdb'             => \&cmd_searchdb,
56   'removedb'             => \&cmd_removedb,
57   'cleandb'              => \&cmd_cleandb,
58   'search'               => \&cmd_search,
59   'enable'               => \&cmd_enable,
60   'disable'              => \&cmd_disable,
61   'status'               => \&cmd_status,
62   'updatesw'             => \&cmd_updatesw,
63   'exportsw'             => \&cmd_exportsw,
64   'iplocation'           => \&cmd_ip_location,
65   'ip-free'              => \&cmd_ip_free,
[35]66   'search-mac-on-switch' => \&cmd_search_mac_on_switch,
[133]67   'bad-vlan-id'          => \&cmd_bad_vlan_id,
68   'set-vlan-port'        => \&cmd_set_vlan_port,
69   'get-vlan-port'        => \&cmd_get_vlan_port,
70   'set-vlan-name'        => \&cmd_set_vlan_name,
71   'get-vlan-name'        => \&cmd_get_vlan_name,
[142]72   'rebootsw'             => \&cmd_rebootsw,
[2]73   );
74
[63]75Readonly my %INTERNAL_PORT_MAP => (
[2]76   0 => 'A',
77   1 => 'B',
78   2 => 'C',
79   3 => 'D',
80   4 => 'E',
81   5 => 'F',
82   6 => 'G',
83   7 => 'H',
84   );
[63]85Readonly my %INTERNAL_PORT_MAP_REV => reverse %INTERNAL_PORT_MAP;
[2]86
[63]87Readonly my %SWITCH_KIND => (
[128]88   # HP
[171]89   J3299A           => { type => 1, model => 'HP224M',         match => 'HP J3299A ProCurve Switch 224M'       },
90   J4120A           => { type => 1, model => 'HP1600M',        match => 'HP J4120A ProCurve Switch 1600M'      },
91   J9029A           => { type => 1, model => 'HP1800-8G',      match => 'PROCURVE J9029A'                      },
92   J9449A           => { type => 1, model => 'HP1810-8G',      match => 'HP ProCurve 1810G - 8 GE'             },
93   J4093A           => { type => 1, model => 'HP2424M',        match => 'HP J4093A ProCurve Switch 2424M'      },
94   J9279A           => { type => 1, model => 'HP2510G-24',     match => 'ProCurve J9279A Switch 2510G-24'      },
95   J9280A           => { type => 1, model => 'HP2510G-48',     match => 'ProCurve J9280A Switch 2510G-48'      },
96   J4813A           => { type => 1, model => 'HP2524',         match => 'HP J4813A ProCurve Switch 2524'       },
97   J4900A           => { type => 1, model => 'HP2626A',        match => 'HP J4900A ProCurve Switch 2626'       },
98   J4900B           => { type => 1, model => 'HP2626B',        match => 'J4900B.+?Switch 2626'                 }, # ProCurve J4900B Switch 2626 # HP J4900B ProCurve Switch 2626
99   J4899B           => { type => 1, model => 'HP2650',         match => 'ProCurve J4899B Switch 2650'          },
100   J9021A           => { type => 1, model => 'HP2810-24G',     match => 'ProCurve J9021A Switch 2810-24G'      },
101   J9022A           => { type => 1, model => 'HP2810-48G',     match => 'ProCurve J9022A Switch 2810-48G'      },
102   J8692A           => { type => 1, model => 'HP3500-24G',     match => 'J8692A Switch 3500yl-24G'             },
103   J4903A           => { type => 1, model => 'HP2824',         match => 'J4903A.+?Switch 2824,'                },
104   J4110A           => { type => 1, model => 'HP8000M',        match => 'HP J4110A ProCurve Switch 8000M'      },
105   JE074A           => { type => 2, model => 'HP5120-24G',     match => 'HP Comware.+?A5120-24G EI Switch'     },
106   JE069A           => { type => 2, model => 'HP5120-48G',     match => 'HP Comware.+?A5120-48G EI Switch'     },
107   JD377A           => { type => 2, model => 'HP5500-24G',     match => 'HP Comware.+?A5500-24G EI Switch'     },
108   JD374A           => { type => 2, model => 'HP5500-24F',     match => 'HP Comware.+?A5500-24G-SFP EI Switch' },
[128]109   # BayStack
[171]110   BS350T           => { type => 1, model => 'BS350T',         match => 'BayStack 350T HW'                     },
[128]111   # Nexans
[171]112   N3483G           => { type => 2, model => 'NA3483-6G',      match => 'GigaSwitch V3 TP SFP-I 48V ES3'       },
[145]113   # DELL
[184]114   PC7024           => { type => 2, model => 'DPC7024',        match => 'PowerConnect 7024,.+?VxWorks'         },
[171]115   N2048            => { type => 2, model => 'DN2048',         match => 'Dell Networking N2048,'               },
[183]116   N4032F           => { type => 2, model => 'DN4032F',        match => 'Dell Networking N4032F,'              },
[171]117   N4064F           => { type => 2, model => 'DN4064F',        match => 'Dell Networking N4064F,'              },
[128]118   # 3COM
[171]119   'H3C5500'        => { type => 1, model => 'H3C5500',        match => 'H3C S5500-SI Series'                  },
120   '3C17203'        => { type => 1, model => '3C17203',        match => '3Com SuperStack 3 24-Port'            },
121   '3C17204'        => { type => 1, model => '3C17204',        match => '3Com SuperStack 3 48-Port'            },
122   '3CR17562-91'    => { type => 1, model => '3CR17562-91',    match => '3Com Switch 4500 50-Port'             },
123   '3CR17255-91'    => { type => 1, model => '3CR17255-91',    match => '3Com Switch 5500G-EI 48-Port'         },
124   '3CR17251-91'    => { type => 1, model => '3CR17251-91',    match => '3Com Switch 5500G-EI 48-Port'         },
125   '3CR17571-91'    => { type => 1, model => '3CR17571-91',    match => '3Com Switch 4500 PWR 26-Port'         },
126   '3CRWX220095A'   => { type => 1, model => '3CRWX220095A',   match => '3Com Wireless LAN Controller'         },
127   '3CR17254-91'    => { type => 1, model => '3CR17254-91',    match => '3Com Switch 5500G-EI 24-Port'         },
128   '3CRS48G-24S-91' => { type => 1, model => '3CRS48G-24S-91', match => '3Com Switch 4800G 24-Port'            },
129   '3CRS48G-48S-91' => { type => 1, model => '3CRS48G-48S-91', match => '3Com Switch 4800G 48-Port'            },
130   '3C17708'        => { type => 1, model => '3C17708',        match => '3Com Switch 4050'                     },
131   '3C17709'        => { type => 1, model => '3C17709',        match => '3Com Switch 4060'                     },
132   '3C17707'        => { type => 1, model => '3C17707',        match => '3Com Switch 4070'                     },
133   '3CR17258-91'    => { type => 1, model => '3CR17258-91',    match => '3Com Switch 5500G-EI 24-Port SFP'     },
134   '3CR17181-91'    => { type => 1, model => '3CR17181-91',    match => '3Com Switch 5500-EI 28-Port FX'       },
135   '3CR17252-91'    => { type => 1, model => '3CR17252-91',    match => '3Com Switch 5500G-EI PWR 24-Port'     },
136   '3CR17253-91'    => { type => 1, model => '3CR17253-91',    match => '3Com Switch 5500G-EI PWR 48-Port'     },
137   '3CR17250-91'    => { type => 1, model => '3CR17250-91',    match => '3Com Switch 5500G-EI 24-Port'         },
138   '3CR17561-91'    => { type => 1, model => '3CR17561-91',    match => '3Com Switch 4500 26-Port'             },
139   '3CR17572-91'    => { type => 1, model => '3CR17572-91',    match => '3Com Switch 4500 PWR 50-Port'         },
140   '3C17702-US'     => { type => 1, model => '3C17702-US',     match => '3Com Switch 4900 SX'                  },
141   '3C17700'        => { type => 1, model => '3C17700',        match => '3Com Switch 4900'                     },
[18]142   );
[63]143
144Readonly my %OID_NUMBER => (
[68]145   sysDescription  => '1.3.6.1.2.1.1.1.0',
146   sysName         => '1.3.6.1.2.1.1.5.0',
147   sysContact      => '1.3.6.1.2.1.1.4.0',
148   sysLocation     => '1.3.6.1.2.1.1.6.0',
[127]149   searchPort1     => '1.3.6.1.2.1.17.4.3.1.2',       # BRIDGE-MIB (802.1D).
[144]150   searchPort2     => '1.3.6.1.2.1.17.7.1.2.2.1.2',   # Q-BRIDGE-MIB (802.1Q) add 0 if unknown vlan id
[129]151   vlanPortDefault => '1.3.6.1.2.1.17.7.1.4.5.1.1',   # dot1qPvid
152   vlanStatus      => '1.3.6.1.2.1.17.7.1.4.3.1.5',   # integer 4 Create, 6 Destroy
153   vlanName        => '1.3.6.1.2.1.17.7.1.4.3.1.1',   # string
[141]154   hpicfReset      => '1.3.6.1.4.1.11.2.14.11.1.4.1', # HP reboot switch
[157]155   ifIndex         => '1.3.6.1.2.1.17.1.4.1.2',       # dot1dBasePortIfIndex - Interface index redirection
[146]156   ifName          => '1.3.6.1.2.1.31.1.1.1.1',       # Interface name (give port number)
[19]157   );
[18]158
[63]159Readonly 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;
160Readonly my $RE_IPv4_ADDRESS => qr{ [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} }xms;
161
[118]162Readonly my $RE_FLOAT_HOSTNAME => qr{ ^float }xms;
[63]163
[118]164
[2]165################
166# principal
167################
168
[11]169my $cmd = shift @ARGV || 'help';
170if (defined $CMD_DB{$cmd}) {
171   $CMD_DB{$cmd}->(@ARGV);
[2]172   }
173else {
[63]174   print {*STDERR} "klask: command $cmd not found\n\n";
[11]175   $CMD_DB{help}->();
[2]176   exit 1;
177   }
178
179exit;
180
[26]181sub test_running_environnement {
182   die "Configuration file $KLASK_CFG_FILE does not exists. Klask need it !\n" if not -e "$KLASK_CFG_FILE";
183   die "Var folder $KLASK_VAR does not exists. Klask need it !\n"              if not -d "$KLASK_VAR";
[63]184   return;
[26]185   }
186
[34]187sub test_switchdb_environnement {
188   die "Switch database $KLASK_SW_FILE does not exists. Launch updatesw before this command !\n" if not -e "$KLASK_SW_FILE";
[63]189   return;
[34]190   }
191
192sub test_maindb_environnement {
193   die "Main database $KLASK_DB_FILE does not exists. Launch updatedb before this command !\n" if not -e "$KLASK_DB_FILE";
[63]194   return;
[34]195   }
196
[2]197###
198# fast ping dont l'objectif est de remplir la table arp de la machine
[111]199sub fast_ping {
[68]200   # Launch this command without waiting...
[132]201   system "fping -q -c 1 @_ >/dev/null 2>&1 &";
[63]202   return;
[2]203   }
204
[63]205sub shell_command {
206   my $cmd = shift;
207
[64]208   my $fh     = new FileHandle;
209   my $result = '';
[96]210   open $fh, q{-|}, "LANG=C $cmd" or die "Can't exec $cmd\n";
[64]211   $result .= <$fh>;
[63]212   close $fh;
213   chomp $result;
214   return $result;
215   }
216
[2]217###
218# donne l'@ ip, dns, arp en fonction du dns OU de l'ip
219sub resolve_ip_arp_host {
220   my $param_ip_or_host = shift;
[63]221   my $interface = shift || q{*};
222   my $type      = shift || q{fast};
[186]223   my $already   = shift || q{yes};
[2]224
225   my %ret = (
226      hostname_fq  => 'unknow',
227      ipv4_address => '0.0.0.0',
228      mac_address  => 'unknow',
229      );
230
[68]231   # perl -MSocket -E 'say inet_ntoa(scalar gethostbyname("tech7meylan.hmg.inpg.fr"))'
232   my $packed_ip = scalar gethostbyname($param_ip_or_host);
233   return %ret if not defined $packed_ip;
234   $ret{ipv4_address} = inet_ntoa($packed_ip);
[197]235   #if ($ret{ipv4_address} !~ m/$RE_IPv4_ADDRESS/) {
236   #   print "Error: for computer $param_ip_or_host on interface $interface, IP $ret{ipv4_address} is not valide\n";
237   #   return %ret;
238   #   }
[68]239
240   # perl -MSocket -E 'say scalar gethostbyaddr(inet_aton("194.254.66.240"), AF_INET)'
[186]241   my $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) if $already eq 'yes';
[68]242   $ret{hostname_fq} = $hostname_fq if defined $hostname_fq;
243
244   # my $cmd = q{grep  -he '\b} . $param_ip_or_host . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
[144]245   #my $cmd = q{grep  -he '\b} . $ret{ipv4_address} . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
246   my $cmd = q{grep  -He '\b} . $ret{ipv4_address} . q{\b' } . "/var/lib/arpwatch/$interface.dat" . '| sed -e \'s|^/var/lib/arpwatch/\(.*\)\.dat:|\1 |;\' | sort -rn -k 4,4 | head -1';
247   #grep -He 194.254.66.252 /var/lib/arpwatch/*.dat | sed -e 's|^/var/lib/arpwatch/\(.*\)\.dat:|\1\t|;' | sort -rn -k 4,4 | head -1
248
[62]249   my $cmd_arpwatch = shell_command $cmd;
[144]250   #my ($arp, $ip, $timestamp, $host) = split m/ \s+ /xms, $cmd_arpwatch;
251   my ($interface2, $arp, $ip, $timestamp, $host) = split m/ \s+ /xms, $cmd_arpwatch;
[44]252
[144]253   $ret{interface}    = $interface2 || $interface;
[2]254   $ret{mac_address}  = $arp       if $arp;
255   $ret{timestamp}    = $timestamp if $timestamp;
256
[63]257   my $nowtimestamp = time;
[3]258
[96]259   if ( $type eq 'fast' and ( not defined $timestamp or $timestamp < ( $nowtimestamp - 45 * 60 ) ) ) { # 45 min
[3]260      $ret{mac_address} = 'unknow';
261      return %ret;
262      }
263
[194]264   # ARP result
265   #
266   # LANG=C arp -a 194.254.66.62 -i eth331
267   # gw66-62.legi.grenoble-inp.fr (194.254.66.62) at 00:08:7c:bb:0f:c0 [ether] on eth331
268   #
269   # LANG=C ip neigh show to 194.254.66.62 dev eth331
270   # 194.254.66.62 lladdr 00:08:7c:bb:0f:c0 REACHABLE
[197]271   # LANG=C ip neigh show to 194.254.66.62
272   # 194.254.66.62 dev eth331 lladdr 00:08:7c:bb:0f:c0 REACHABLE
[194]273#   my $cmd_arp  = shell_command "arp -a $param_ip_or_host -i $ret{interface}";
274#   if ( $cmd_arp =~ m{ (\S*) \s \( ( $RE_IPv4_ADDRESS ) \) \s at \s ( $RE_MAC_ADDRESS ) }xms ) {
275#      ( $ret{hostname_fq}, $ret{ipv4_address}, $ret{mac_address} )  = ($1, $2, $3);
276#      }
277   if ($ret{mac_address} eq 'unknow') {
[196]278      # Last chance to have the mac_address
[197]279      if ($ret{interface} eq '*') {
280         my $cmd_arp  = shell_command "ip neigh show to $ret{ipv4_address}";
281         if ( $cmd_arp =~ m{ ^$RE_IPv4_ADDRESS \s dev \s ([\w\d\.\:]+) \s lladdr \s ( $RE_MAC_ADDRESS ) \s }xms ) {
282            ($ret{interface}, $ret{mac_address}) = ($1, $2);
283            }
[194]284         }
[197]285      else {
286         my $cmd_arp  = shell_command "ip neigh show to $ret{ipv4_address} dev $ret{interface}";
287         if ( $cmd_arp =~ m{ ^$RE_IPv4_ADDRESS \s lladdr \s ( $RE_MAC_ADDRESS ) \s }xms ) {
288            $ret{mac_address} = $1;
289            }
290         }
[63]291      }
[2]292
[96]293   # Normalize MAC Address
[63]294   if ($ret{mac_address} ne 'unknow') {
[2]295      my @paquets = ();
[165]296      for ( split m/ : /xms, $ret{mac_address} ) {
[63]297         my @chars = split m//xms, uc "00$_";
[2]298         push @paquets, "$chars[-2]$chars[-1]";
299         }
[63]300      $ret{mac_address} = join q{:}, @paquets;
[2]301      }
302
303   return %ret;
304   }
305
[20]306# Find Surname of a switch
307sub get_switch_model {
[22]308   my $sw_snmp_description = shift || 'unknow';
[169]309   $sw_snmp_description =~ s/[\n\r]/ /g;
[63]310
[20]311   for my $sw_kind (keys %SWITCH_KIND) {
[64]312      next if not $sw_snmp_description =~ m/$SWITCH_KIND{$sw_kind}->{match}/ms; # option xms break search, why ?
[63]313
[20]314      return $SWITCH_KIND{$sw_kind}->{model};
315      }
[63]316
[22]317   return $sw_snmp_description;
[20]318   }
319
[2]320###
321# va rechercher le nom des switchs pour savoir qui est qui
[4]322sub init_switch_names {
[173]323   my ($verbose, $verb_description, $check_hostname, $check_location) = @_;
[63]324
[133]325   printf "%-26s                %-25s %s\n",'Switch','Description','Type' if $verbose;
[82]326   print "------------------------------------------------------------------------------\n" if $verbose;
[2]327
328   INIT_EACH_SWITCH:
[196]329   for my $sw (@SWITCH_LIST) {
[3]330      my %session = ( -hostname   => $sw->{hostname} );
331         $session{-version} = $sw->{version}   || 1;
[11]332         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
[63]333         if (exists $sw->{version} and $sw->{version} eq '3') {
[3]334            $session{-username} = $sw->{username} || 'snmpadmin';
335            }
336         else {
[11]337            $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
[3]338            }
339
340      $sw->{local_session} = \%session;
341
342      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
[14]343      print "$error \n" if $error;
344
[2]345      my $result = $session->get_request(
[18]346         -varbindlist => [
[63]347            $OID_NUMBER{sysDescription},
[18]348            $OID_NUMBER{sysName},
349            $OID_NUMBER{sysContact},
350            $OID_NUMBER{sysLocation},
351            ]
[2]352         );
[196]353      if (!defined $result) {
354         printf {*STDERR} "ERROR: %s.\n", $session->error();
355         $session->close();
356         # Remove bad switch
357         @SWITCH_LIST = grep { $_->{hostname} ne $sw->{hostname} } @SWITCH_LIST;
358         delete $SWITCH_LEVEL{$sw->{hostname}} if exists $SWITCH_LEVEL{$sw->{hostname}};
359         delete $SWITCH_DB{$sw->{hostname}}    if exists $SWITCH_DB{$sw->{hostname}};
360         next INIT_EACH_SWITCH;
361         }
362
[18]363      $sw->{description} = $result->{$OID_NUMBER{sysName}} || $sw->{hostname};
[170]364      $sw->{model} = get_switch_model($result->{$OID_NUMBER{sysDescription}});
365      if ($verb_description) {
366         my $desc = $result->{$OID_NUMBER{sysDescription}};
367         $desc =~ s/[\n\r]/ /g;
368         print "   description: $desc\n"
369         }
[172]370      if ($check_hostname) {
371         my ($hostname) = split /\./, $sw->{hostname}, 2;
[175]372         print " $hostname - error internal hostname: $sw->{hostname}\n" if $result->{$OID_NUMBER{sysName}} ne $hostname;
[172]373         }
[173]374      if ($check_location) {
[174]375         my $location = $result->{$OID_NUMBER{sysLocation}};
376         $location =~ s/^"(.+)"$/$1/;
[175]377         print " $sw->{hostname} - error location: '$location' -> '$sw->{location}'\n" if $location ne $sw->{location};
[173]378         }
[3]379      #$sw->{location} = $result->{"1.3.6.1.2.1.1.6.0"} || $sw->{hostname};
380      #$sw->{contact} = $result->{"1.3.6.1.2.1.1.4.0"} || $sw->{hostname};
[2]381      $session->close;
[22]382
[63]383      # Ligne à virer car on récupère maintenant le modèle du switch
[196]384      #my ($desc, $type) = split m/ : /xms, $sw->{description}, 2;
[197]385      printf "%-26s 0--------->>>> %-25s %s\n", $sw->{hostname}, $sw->{description}, $sw->{model} if $verbose;
[2]386      }
387
388   print "\n" if $verbose;
[63]389   return;
[2]390   }
391
392###
393# convertit l'hexa (uniquement 2 chiffres) en decimal
[86]394sub digit_hex_to_dec {
[2]395   #00:0F:1F:43:E4:2B
[63]396   my $car = '00' . uc shift;
[2]397
[4]398   return '00' if $car eq '00UNKNOW';
[2]399   my %table = (
[62]400      '0'=>'0',  '1'=>'1',  '2'=>'2',  '3'=>'3',  '4'=>'4',
401      '5'=>'5',  '6'=>'6',  '7'=>'7',  '8'=>'8',  '9'=>'9',
[63]402      'A'=>'10', 'B'=>'11', 'C'=>'12', 'D'=>'13', 'E'=>'14', 'F'=>'15',
[2]403      );
[63]404   my @chars = split m//xms, $car;
[2]405   return $table{$chars[-2]}*16 + $table{$chars[-1]};
406   }
407
[136]408#--------------------------------------------------------------------------------
409
410sub normalize_mac_address {
411   my $mac_address = shift;
412
[139]413   # D07E-28D1-7AB8 or d07e28-d17ab8
414   if ($mac_address =~ m{^ (?: [0-9A-Fa-f]{4} -){2} [0-9A-Fa-f]{4} $}xms
415      or $mac_address =~ m{^ [0-9A-Fa-f]{6} - [0-9A-Fa-f]{6} $}xms) {
416      $mac_address =~ s/-//g;
417      return join q{:}, unpack('(A2)*', uc($mac_address));
418      }
419
[136]420   return join q{:}, map { substr( uc("00$_"), -2) } split m/ [:-] /xms, $mac_address;
421   }
422
423#--------------------------------------------------------------------------------
[86]424# convertit l'@ mac en decimal
425sub mac_address_hex_to_dec {
[2]426   #00:0F:1F:43:E4:2B
[86]427   my $mac_address = shift;
[2]428
[86]429   my @paquets = split m/ : /xms, $mac_address;
[63]430   my $return = q{};
[165]431   for (@paquets) {
[86]432      $return .= q{.} . digit_hex_to_dec($_);
[2]433      }
434   return $return;
435   }
436
437###
438# va rechercher le port et le switch sur lequel est la machine
439sub find_switch_port {
[86]440   my $mac_address     = shift;
[63]441   my $switch_proposal = shift || q{};
[144]442   my $vlan_id = shift || 0;
[63]443
[2]444   my %ret;
[63]445   $ret{switch_description} = 'unknow';
446   $ret{switch_port} = '0';
[2]447
[86]448   return %ret if $mac_address eq 'unknow';;
[2]449
[196]450   my @switch_search = @SWITCH_LIST;
[63]451   if ($switch_proposal ne q{}) {
[196]452      for my $sw (@SWITCH_LIST) {
[2]453         next if $sw->{hostname} ne $switch_proposal;
[22]454         unshift @switch_search, $sw;
[2]455         last;
456         }
457      }
458
[121]459   my $research1 = $OID_NUMBER{searchPort1} . mac_address_hex_to_dec($mac_address);
[144]460   my $research2 = $OID_NUMBER{searchPort2} .'.'. $vlan_id . mac_address_hex_to_dec($mac_address);
[63]461
[2]462   LOOP_ON_SWITCH:
[22]463   for my $sw (@switch_search) {
[3]464      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
[22]465      print "$error \n" if $error;
466
[2]467      my $result = $session->get_request(
[124]468         -varbindlist => [$research1]
[2]469         );
[124]470      if (not defined $result) {
471         $result = $session->get_request(
472            -varbindlist => [$research2]
473            );
474         $result->{$research1} = $result->{$research2} if defined $result;
475         }
476
477      if (not (defined $result and $result->{$research1} ne 'noSuchInstance')) {
[2]478         $session->close;
479         next LOOP_ON_SWITCH;
480         }
481
[167]482      my $swport_num = $result->{$research1};
483      my $swport_hr = get_human_readable_port($sw->{model}, snmp_get_swithport_hr($session, $swport_num));
[2]484
[150]485      $session->close;
486
487      # IMPORTANT !!
488      # ceci empeche la detection sur certains port ...
489      # en effet les switch sont relies entre eux par un cable reseau et du coup
490      # tous les arp de toutes les machines sont presentes sur ces ports (ceux choisis ici sont les miens)
491      # cette partie est a ameliore, voir a configurer dans l'entete
492      # 21->24 45->48
493      SWITCH_PORT_IGNORE:
[167]494      for my $portignore (@{$sw->{portignore}}) {
495         next LOOP_ON_SWITCH if $swport_hr eq $portignore;
[150]496         }
497
498      $ret{switch_hostname}    = $sw->{hostname};
499      $ret{switch_description} = $sw->{description};
[167]500      $ret{switch_port}        = $swport_num;
[150]501      $ret{switch_port_hr}     = $swport_hr; # human readable
502
503      last LOOP_ON_SWITCH;
[2]504      }
505   return %ret;
506   }
507
508###
509# va rechercher les port et les switch sur lequel est la machine
510sub find_all_switch_port {
[86]511   my $mac_address = shift;
[144]512   my $vlan_id     = shift || 0;
[2]513
514   my $ret = {};
515
[86]516   return $ret if $mac_address eq 'unknow';
[2]517
[121]518   my $research1 = $OID_NUMBER{searchPort1} . mac_address_hex_to_dec($mac_address);
[144]519   my $research2 = $OID_NUMBER{searchPort2} .'.'. $vlan_id . mac_address_hex_to_dec($mac_address);
[2]520   LOOP_ON_ALL_SWITCH:
[196]521   for my $sw (@SWITCH_LIST) {
[3]522      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
[13]523      print "$error \n" if $error;
524
[2]525      my $result = $session->get_request(
[124]526         -varbindlist => [$research1]
[2]527         );
[124]528      if (not defined $result) {
529         $result = $session->get_request(
530            -varbindlist => [$research2]
531            );
532         $result->{$research1} = $result->{$research2} if defined $result;
533         }
[13]534
[124]535      if (defined $result and $result->{$research1} ne 'noSuchInstance') {
[167]536         my $swport_num = $result->{$research1};
537         my $swport_hr = get_human_readable_port($sw->{model}, snmp_get_swithport_hr($session, $swport_num));
[2]538
[181]539         SWITCH_PORT_IGNORE:
540         for my $portignore (@{$sw->{portignore}}) {
541            if ($swport_hr eq $portignore) {
542               $session->close;
543               next LOOP_ON_ALL_SWITCH
544               }
545            }
546
[2]547         $ret->{$sw->{hostname}} = {};
548         $ret->{$sw->{hostname}}{hostname}    = $sw->{hostname};
549         $ret->{$sw->{hostname}}{description} = $sw->{description};
[167]550         $ret->{$sw->{hostname}}{port}        = $swport_num;
[154]551         $ret->{$sw->{hostname}}{port_hr}     = $swport_hr;
[2]552
[86]553#         $SWITCH_PORT_COUNT{$sw->{hostname}}->{$swport}++;
[2]554         }
555
556      $session->close;
557      }
558   return $ret;
559   }
560
561sub get_list_network {
562
[12]563   return keys %{$KLASK_CFG->{network}};
[2]564   }
565
566sub get_current_interface {
[113]567   my $vlan_name = shift;
[2]568
[113]569   return $KLASK_CFG->{network}{$vlan_name}{interface};
[2]570   }
[63]571
[144]572sub get_current_vlan_id {
573   my $vlan_name = shift;
574
[182]575   return 0 if not exists $KLASK_CFG->{network}{$vlan_name};
[144]576   return $KLASK_CFG->{network}{$vlan_name}{'vlan-id'};
577   }
578
[179]579sub get_current_scan_mode {
580   my $vlan_name = shift;
581
582   return $KLASK_CFG->{network}{$vlan_name}{'scan-mode'} || $DEFAULT{'scan-mode'} || 'active';
583   }
584
[144]585sub get_current_vlan_name_for_interface {
586   my $interface = shift;
587
588   for my $vlan_name (keys %{$KLASK_CFG->{network}}) {
589      next if $KLASK_CFG->{network}{$vlan_name}{interface} ne $interface;
590      return $vlan_name;
591      }
592   }
593
[2]594###
595# liste l'ensemble des adresses ip d'un réseau
596sub get_list_ip {
[113]597   my @vlan_name = @_;
[2]598
599   my $cidrlist = Net::CIDR::Lite->new;
600
[113]601   for my $net (@vlan_name) {
[12]602      my @line  = @{$KLASK_CFG->{network}{$net}{'ip-subnet'}};
[2]603      for my $cmd (@line) {
[63]604         for my $method (keys %{$cmd}){
[2]605            $cidrlist->add_any($cmd->{$method}) if $method eq 'add';
606            }
607         }
608      }
609
[4]610   my @res = ();
[2]611
612   for my $cidr ($cidrlist->list()) {
613      my $net = new NetAddr::IP $cidr;
[63]614      for my $ip (@{$net}) {
615         $ip =~ s{ /32 }{}xms;
[2]616         push @res,  $ip;
617         }
618      }
619
620   return @res;
621   }
622
[9]623# liste l'ensemble des routeurs du réseau
624sub get_list_main_router {
[113]625   my @vlan_name = @_;
[9]626
627   my @res = ();
628
[113]629   for my $net (@vlan_name) {
[12]630      push @res, $KLASK_CFG->{network}{$net}{'main-router'};
[9]631      }
632
633   return @res;
634   }
635
[2]636sub get_human_readable_port {
[22]637   my $sw_model = shift;
638   my $sw_port  = shift;
[63]639
[167]640   # Not need anymore
641   # get port name by snmp
642   return $sw_port;
643
[64]644   if ($sw_model eq 'HP8000M') {
[63]645
[64]646      my $reste = (($sw_port - 1) % 8) + 1;
647      my $major = int (($sw_port - 1) / 8);
648      return "$INTERNAL_PORT_MAP{$major}$reste";
649      }
[2]650
[64]651   if ($sw_model eq 'HP2424M') {
652      if ($sw_port > 24) {
[198]653
[64]654         my $reste = $sw_port - 24;
655         return "A$reste";
656         }
657      }
658
659   if ($sw_model eq 'HP1600M') {
660      if ($sw_port > 16) {
[198]661
[64]662         my $reste = $sw_port - 16;
663         return "A$reste";
664         }
665      }
666
[116]667   if ($sw_model eq 'HP2810-48G' or $sw_model eq 'HP2810-24G') {
[115]668      if ($sw_port > 48) {
[198]669
[115]670         my $reste = $sw_port - 48;
[116]671         return "Trk$reste";
[115]672         }
673      }
674
[118]675   if ($sw_model eq 'HP3500-24G') {
676      if ($sw_port > 289) {
[198]677
[118]678         my $reste = $sw_port - 289;
679         return "Trk$reste";
680         }
681      }
682
[64]683   return $sw_port;
[2]684   }
685
686sub get_numerical_port {
[22]687   my $sw_model = shift;
688   my $sw_port  = shift;
[63]689
[64]690   if ($sw_model eq 'HP8000M') {
[2]691
[64]692      my $letter = substr $sw_port, 0, 1;
693      my $reste =  substr $sw_port, 1;
[63]694
[64]695      return $INTERNAL_PORT_MAP_REV{$letter} * 8 + $reste;
696      }
[63]697
[64]698   if ($sw_model eq 'HP2424M') {
699      if ($sw_port =~ m/^A/xms ) {
[63]700
[64]701         my $reste =  substr $sw_port, 1;
702
703         return 24 + $reste;
704         }
705      }
706
707   if ($sw_model eq 'HP1600M') {
708      if ($sw_port =~ m/^A/xms ) {
709
710         my $reste =  substr $sw_port, 1;
711
712         return 16 + $reste;
713         }
714      }
715
[116]716   if ($sw_model eq 'HP2810-48G' or $sw_model eq 'HP2810-24G') {
717      if ($sw_port =~ m/^Trk/xms ) {
[115]718
[116]719         my $reste =  substr $sw_port, 3;
[115]720
721         return 48 + $reste;
722         }
723      }
724
[118]725   if ($sw_model eq 'HP3500-24G') {
726      if ($sw_port =~ m/^Trk/xms ) {
727
728         my $reste =  substr $sw_port, 3;
729
730         return 289 + $reste;
731         }
732      }
733
[64]734   return $sw_port;
[2]735   }
736
[150]737sub get_port_human_readable_short {
738   my $sw_port_hr  = shift;
739
[162]740   $sw_port_hr =~ s/^Bridge-Aggregation/Br/i;
741   $sw_port_hr =~ s/^Port-Channel/Po/i;
742   $sw_port_hr =~ s/^Forty-?GigabitEthernet/Fo/i;
743   $sw_port_hr =~ s/^Ten-?GigabitEthernet/Te/i;
744   $sw_port_hr =~ s/^GigabitEthernet/Gi/i;
745   $sw_port_hr =~ s/^FastEthernet/Fa/i;
[150]746
747   return $sw_port_hr;
748   }
749
[154]750sub snmp_get_swithport_hr {
751   my ($snmp_session, $swport) = @_;
[198]752
[157]753   my $research_index = $OID_NUMBER{ifIndex} .'.'. $swport;
754   my $result_index = $snmp_session->get_request(
755      -varbindlist => [$research_index]
756      );
757   my $swifindex = $swport;
[159]758   $swifindex = $result_index->{$research_index} if defined $result_index;
[157]759
760   my $research_hr = $OID_NUMBER{ifName} .'.'. $swifindex;
761   my $result_hr = $snmp_session->get_request(
[154]762      -varbindlist => [$research_hr]
763      );
764   my $swport_hr = $swport;
[158]765   $swport_hr = get_port_human_readable_short($result_hr->{$research_hr}) if defined $result_hr;
[154]766   return $swport_hr;
767   }
768
[151]769# Load computer database
[150]770sub computerdb_load {
771   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
[151]772
773   LOOP_ON_IP_ADDRESS:
774   for my $ip (keys %{$computerdb}) {
775
[185]776      next LOOP_ON_IP_ADDRESS if exists $computerdb->{$ip}{switch_port_hr} and defined $computerdb->{$ip}{switch_port_hr};
[198]777
[151]778      $computerdb->{$ip}{switch_port_hr} = $computerdb->{$ip}{switch_port};
779      }
780
[150]781   return $computerdb;
782   }
783
[2]784################
785# Les commandes
786################
787
788sub cmd_help {
789
[63]790print <<'END';
[2]791klask - ports manager and finder for switch
792
[129]793 klask version
794
[2]795 klask updatedb
[69]796 klask exportdb --format [txt|html]
[76]797 klask removedb computer*
[190]798 klask cleandb  --day number_of_day --repair-dns --verbose
[2]799
[45]800 klask updatesw
[69]801 klask exportsw --format [txt|dot]
[45]802
[2]803 klask searchdb computer
804 klask search   computer
[68]805 klask search-mac-on-switch switch mac_addr
[2]806
[76]807 klask ip-free --day number_of_day --format [txt|html] [vlan_name]
[69]808
[133]809 klask bad-vlan-id
[129]810
[2]811 klask enable  switch port
812 klask disable switch port
813 klask status  switch port
814END
[63]815   return;
[2]816   }
817
[36]818sub cmd_version {
819
[63]820print <<'END';
[36]821Klask - ports manager and finder for switch
[195]822Copyright (C) 2005-2017 Gabriel Moreau
[36]823
824END
[37]825   print ' $Id: klask 202 2017-01-29 15:27:43Z g7moreau $'."\n";
[63]826   return;
[36]827   }
828
[2]829sub cmd_search {
830   my @computer = @_;
[63]831
[4]832   init_switch_names();    #nomme les switchs
[111]833   fast_ping(@computer);
[133]834
835   LOOP_ON_COMPUTER:
[2]836   for my $clientname (@computer) {
837      my %resol_arp = resolve_ip_arp_host($clientname);          #resolution arp
[144]838      my $vlan_name = get_current_vlan_name_for_interface($resol_arp{interface});
839      my $vlan_id   = get_current_vlan_id($vlan_name);
840      my %where     = find_switch_port($resol_arp{mac_address}, '', $vlan_id); #retrouve l'emplacement
[133]841
842      next LOOP_ON_COMPUTER if $where{switch_description} eq 'unknow' or $resol_arp{hostname_fq} eq 'unknow' or $resol_arp{mac_address} eq 'unknow';
843
[146]844      printf '%-22s %2s %-30s %-15s %18s',
[134]845         $where{switch_hostname},
[150]846         $where{switch_port_hr},
[133]847         $resol_arp{hostname_fq},
848         $resol_arp{ipv4_address},
849         $resol_arp{mac_address}."\n";
[2]850      }
[63]851   return;
[2]852   }
853
854sub cmd_searchdb {
[136]855   my @ARGV  = @_;
856
857   my $kind;
858
[138]859   GetOptions(
[137]860      'kind=s'   => \$kind,
[136]861      );
862
863   my %possible_search = (
864      host  => \&cmd_searchdb_host,
865      mac   => \&cmd_searchdb_mac,
866      );
867
[137]868   $kind = 'host' if not defined $possible_search{$kind};
[136]869
870   $possible_search{$kind}->(@ARGV);
871   return;
872   }
873
874
875sub cmd_searchdb_host {
[2]876   my @computer = @_;
877
[111]878   fast_ping(@computer);
[151]879   my $computerdb = computerdb_load();
[63]880
[2]881   LOOP_ON_COMPUTER:
882   for my $clientname (@computer) {
883      my %resol_arp = resolve_ip_arp_host($clientname);      #resolution arp
884      my $ip = $resol_arp{ipv4_address};
[63]885
[2]886      next LOOP_ON_COMPUTER unless exists $computerdb->{$ip};
[63]887
888      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
[2]889      $year += 1900;
890      $mon++;
[63]891      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
[2]892
893      printf "%-22s %2s %-30s %-15s %-18s %s\n",
[134]894         $computerdb->{$ip}{switch_hostname},
[150]895         $computerdb->{$ip}{switch_port_hr},
[2]896         $computerdb->{$ip}{hostname_fq},
897         $ip,
898         $computerdb->{$ip}{mac_address},
899         $date;
900      }
[63]901   return;
[2]902   }
903
[136]904sub cmd_searchdb_mac {
905   my @mac = map { normalize_mac_address($_) } @_;
906
[151]907   my $computerdb = computerdb_load();
[136]908
909   LOOP_ON_MAC:
910   for my $mac (@mac) {
911      LOOP_ON_COMPUTER:
912      for my $ip (keys %{$computerdb}) {
913         next LOOP_ON_COMPUTER if $mac ne $computerdb->{$ip}{mac_address};
[198]914
[136]915         my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
916         $year += 1900;
917         $mon++;
918         my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
919
920         printf "%-22s %2s %-30s %-15s %-18s %s\n",
921            $computerdb->{$ip}{switch_hostname},
[150]922            $computerdb->{$ip}{switch_port_hr},
[136]923            $computerdb->{$ip}{hostname_fq},
924            $ip,
925            $computerdb->{$ip}{mac_address},
926            $date;
[138]927         #next LOOP_ON_MAC;
[136]928         }
929
930      }
931   return;
932   }
933
[2]934sub cmd_updatedb {
[170]935   @ARGV = @_;
936
[173]937   my ($verbose, $verb_description, $check_hostname, $check_location);
[170]938
939   GetOptions(
[172]940      'verbose|v'          => \$verbose,
941      'verb-description|d' => \$verb_description,
942      'chk-hostname|h'     => \$check_hostname,
[173]943      'chk-location|l'     => \$check_location,
[170]944      );
945
946   my @network = @ARGV;
[2]947      @network = get_list_network() if not @network;
948
[34]949   test_switchdb_environnement();
950
951   my $computerdb = {};
[151]952      $computerdb = computerdb_load() if -e "$KLASK_DB_FILE";
[2]953   my $timestamp = time;
[22]954
[2]955   my %computer_not_detected = ();
956   my $timestamp_last_week = $timestamp - (3600 * 24 * 7);
957
958   my $number_of_computer = get_list_ip(@network); # + 1;
[63]959   my $size_of_database   = keys %{$computerdb};
[31]960      $size_of_database   = 1 if $size_of_database == 0;
[2]961   my $i = 0;
962   my $detected_computer = 0;
[22]963
[173]964   init_switch_names('yes', $verb_description, $check_hostname, $check_location);    #nomme les switchs
[2]965
[22]966   { # Remplis le champs portignore des ports d'inter-connection pour chaque switch
[44]967   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
[22]968   my %db_switch_output_port       = %{$switch_connection->{output_port}};
969   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
970   my %db_switch_chained_port = ();
[63]971   for my $swport (keys %db_switch_connected_on_port) {
972      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[22]973      $db_switch_chained_port{$sw_connect} .= "$port_connect:";
974      }
[196]975   for my $sw (@SWITCH_LIST){
[22]976      push @{$sw->{portignore}}, $db_switch_output_port{$sw->{hostname}}  if exists $db_switch_output_port{$sw->{hostname}};
977      if ( exists $db_switch_chained_port{$sw->{hostname}} ) {
978         chop $db_switch_chained_port{$sw->{hostname}};
[63]979         push @{$sw->{portignore}}, split m/ : /xms, $db_switch_chained_port{$sw->{hostname}};
[22]980         }
981#      print "$sw->{hostname} ++ @{$sw->{portignore}}\n";
982      }
983   }
984
[2]985   my %router_mac_ip = ();
986   DETECT_ALL_ROUTER:
[9]987#   for my $one_router ('194.254.66.254') {
988   for my $one_router ( get_list_main_router(@network) ) {
[2]989      my %resol_arp = resolve_ip_arp_host($one_router);
990      $router_mac_ip{ $resol_arp{mac_address} } = $resol_arp{ipv4_address};
991      }
992
993   ALL_NETWORK:
[179]994   for my $current_net (@network) {
[2]995
[179]996      my @computer = get_list_ip($current_net);
997      my $current_interface = get_current_interface($current_net);
[2]998
[179]999      fast_ping(@computer) if get_current_scan_mode($current_net) eq 'active';
[2]1000
1001      LOOP_ON_COMPUTER:
1002      for my $one_computer (@computer) {
1003         $i++;
[49]1004
[63]1005         my $total_percent = int (($i*100)/$number_of_computer);
1006
[2]1007         my $localtime = time - $timestamp;
[63]1008         my ($sec,$min) = localtime $localtime;
[2]1009
1010         my $time_elapse = 0;
1011            $time_elapse = $localtime * ( 100 - $total_percent) / $total_percent if $total_percent != 0;
[63]1012         my ($sec_elapse,$min_elapse) = localtime $time_elapse;
[2]1013
1014         printf "\rComputer scanned: %4i/%i (%2i%%)",  $i,                 $number_of_computer, $total_percent;
[63]1015         printf ', detected: %4i/%i (%2i%%)', $detected_computer, $size_of_database,   int(($detected_computer*100)/$size_of_database);
1016         printf ' [Time: %02i:%02i / %02i:%02i]', int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60;
[96]1017         printf ' %-8s %-14s', $current_interface, $one_computer;
[2]1018
[186]1019         my $already_exist = exists $computerdb->{$one_computer} ? 'yes' : 'no';
1020         my %resol_arp = resolve_ip_arp_host($one_computer, $current_interface, 'fast', $already_exist);
[63]1021
[9]1022         # do not search on router connection (why ?)
[2]1023         if ( exists $router_mac_ip{$resol_arp{mac_address}}) {
[179]1024            $computer_not_detected{$one_computer} = $current_net;
[2]1025            next LOOP_ON_COMPUTER;
1026            }
1027
[9]1028         # do not search on switch inter-connection
[196]1029         if (exists $SWITCH_LEVEL{$resol_arp{hostname_fq}}) {
[179]1030            $computer_not_detected{$one_computer} = $current_net;
[2]1031            next LOOP_ON_COMPUTER;
1032            }
1033
[63]1034         my $switch_proposal = q{};
[2]1035         if (exists $computerdb->{$resol_arp{ipv4_address}} and exists $computerdb->{$resol_arp{ipv4_address}}{switch_hostname}) {
1036            $switch_proposal = $computerdb->{$resol_arp{ipv4_address}}{switch_hostname};
1037            }
1038
[3]1039         # do not have a mac address
1040         if ($resol_arp{mac_address} eq 'unknow' or (exists $resol_arp{timestamps} and $resol_arp{timestamps} < ($timestamp - 3 * 3600))) {
[179]1041            $computer_not_detected{$one_computer} = $current_net;
[3]1042            next LOOP_ON_COMPUTER;
1043            }
1044
[147]1045         my $vlan_name = get_current_vlan_name_for_interface($resol_arp{interface});
1046         my $vlan_id   = get_current_vlan_id($vlan_name);
1047         my %where = find_switch_port($resol_arp{mac_address},$switch_proposal,$vlan_id);
[2]1048
1049         #192.168.24.156:
1050         #  arp: 00:0B:DB:D5:F6:65
1051         #  hostname: pcroyon.hmg.priv
1052         #  port: 5
1053         #  switch: sw-batH-legi:hp2524
1054         #  timestamp: 1164355525
1055
1056         # do not have a mac address
[3]1057#         if ($resol_arp{mac_address} eq 'unknow') {
1058#            $computer_not_detected{$one_computer} = $current_interface;
1059#            next LOOP_ON_COMPUTER;
1060#            }
[2]1061
1062         # detected on a switch
1063         if ($where{switch_description} ne 'unknow') {
1064            $detected_computer++;
1065            $computerdb->{$resol_arp{ipv4_address}} = {
1066               hostname_fq        => $resol_arp{hostname_fq},
1067               mac_address        => $resol_arp{mac_address},
1068               switch_hostname    => $where{switch_hostname},
1069               switch_description => $where{switch_description},
1070               switch_port        => $where{switch_port},
[150]1071               switch_port_hr     => $where{switch_port_hr},
[2]1072               timestamp          => $timestamp,
[179]1073               network            => $current_net,
[2]1074               };
1075            next LOOP_ON_COMPUTER;
1076            }
1077
1078         # new in the database but where it is ?
1079         if (not exists $computerdb->{$resol_arp{ipv4_address}}) {
1080            $detected_computer++;
1081            $computerdb->{$resol_arp{ipv4_address}} = {
1082               hostname_fq        => $resol_arp{hostname_fq},
1083               mac_address        => $resol_arp{mac_address},
1084               switch_hostname    => $where{switch_hostname},
1085               switch_description => $where{switch_description},
1086               switch_port        => $where{switch_port},
[150]1087               switch_port_hr     => $where{switch_port_hr},
[2]1088               timestamp          => $resol_arp{timestamp},
[179]1089               network            => $current_net,
[2]1090               };
1091            }
1092
1093         # mise a jour du nom de la machine si modification dans le dns
1094         $computerdb->{$resol_arp{ipv4_address}}{hostname_fq} = $resol_arp{hostname_fq};
[63]1095
[2]1096         # mise à jour de la date de détection si détection plus récente par arpwatch
1097         $computerdb->{$resol_arp{ipv4_address}}{timestamp}   = $resol_arp{timestamp} if exists $resol_arp{timestamp} and $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $resol_arp{timestamp};
1098
1099         # relance un arping sur la machine si celle-ci n'a pas été détectée depuis plus d'une semaine
1100#         push @computer_not_detected, $resol_arp{ipv4_address} if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
[179]1101         $computer_not_detected{$resol_arp{ipv4_address}} = $current_net if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
[63]1102
[2]1103         }
1104      }
1105
1106   # final end of line at the end of the loop
1107   printf "\n";
1108
[13]1109   my $dirdb = $KLASK_DB_FILE;
[63]1110      $dirdb =~ s{ / [^/]* $}{}xms;
[2]1111   mkdir "$dirdb", 0755 unless -d "$dirdb";
[44]1112   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
[2]1113
1114   for my $one_computer (keys %computer_not_detected) {
[179]1115      my $current_net = $computer_not_detected{$one_computer};
1116      my $current_interface = get_current_interface($current_net);
1117      system "arping -c 1 -w 1 -rR -i $current_interface $one_computer &>/dev/null" if get_current_scan_mode($current_net) eq 'active';
1118#      print  "arping -c 1 -w 1 -rR -i $current_interface $one_computer 2>/dev/null\n";
[2]1119      }
[63]1120   return;
[2]1121   }
1122
1123sub cmd_removedb {
1124   my @computer = @_;
[34]1125
1126   test_maindb_environnement();
1127
[151]1128   my $computerdb = computerdb_load();
[2]1129
1130   LOOP_ON_COMPUTER:
1131   for my $one_computer (@computer) {
1132
[66]1133      if ( $one_computer =~ m/^ $RE_IPv4_ADDRESS $/xms
1134            and exists $computerdb->{$one_computer} ) {
1135         delete $computerdb->{$one_computer};
1136         next;
1137         }
1138
[2]1139      my %resol_arp = resolve_ip_arp_host($one_computer);
1140
1141      delete $computerdb->{$resol_arp{ipv4_address}} if exists $computerdb->{$resol_arp{ipv4_address}};
1142      }
1143
[13]1144   my $dirdb = $KLASK_DB_FILE;
[63]1145      $dirdb =~ s{ / [^/]* $}{}xms;
[2]1146   mkdir "$dirdb", 0755 unless -d "$dirdb";
[44]1147   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
[63]1148   return;
[2]1149   }
1150
[74]1151sub cmd_cleandb {
[110]1152   my @ARGV  = @_;
[74]1153
1154   my $days_to_clean = 15;
[188]1155   my $repairdns;
[74]1156   my $verbose;
1157   my $database_has_changed;
1158
[138]1159   GetOptions(
[74]1160      'day|d=i'   => \$days_to_clean,
1161      'verbose|v' => \$verbose,
[190]1162      'repair-dns|r' => \$repairdns,
[74]1163      );
1164
1165   my @vlan_name = get_list_network();
1166
[188]1167   my $computerdb = computerdb_load();
[74]1168   my $timestamp = time;
1169
1170   my $timestamp_barrier = 3600 * 24 * $days_to_clean;
[106]1171   my $timestamp_3month  = 3600 * 24 * 90;
[74]1172
[104]1173   my %mactimedb = ();
[74]1174   ALL_VLAN:
[109]1175   for my $vlan (shuffle @vlan_name) {
[74]1176
[109]1177      my @ip_list   = shuffle get_list_ip($vlan);
[198]1178
[74]1179      LOOP_ON_IP_ADDRESS:
1180      for my $ip (@ip_list) {
1181
1182         next LOOP_ON_IP_ADDRESS if
1183            not exists $computerdb->{$ip};
[198]1184
[74]1185            #&& $computerdb->{$ip}{timestamp} > $timestamp_barrier;
[104]1186         my $ip_timestamp   = $computerdb->{$ip}{timestamp};
1187         my $ip_mac         = $computerdb->{$ip}{mac_address};
1188         my $ip_hostname_fq = $computerdb->{$ip}{hostname_fq};
1189
1190         $mactimedb{$ip_mac} ||= {
1191            ip          => $ip,
1192            timestamp   => $ip_timestamp,
1193            vlan        => $vlan,
1194            hostname_fq => $ip_hostname_fq,
1195            };
[198]1196
[108]1197         if (
1198            ( $mactimedb{$ip_mac}->{timestamp} - $ip_timestamp > $timestamp_barrier
1199               or (
1200                  $mactimedb{$ip_mac}->{timestamp} > $ip_timestamp
1201                  and $timestamp - $mactimedb{$ip_mac}->{timestamp} > $timestamp_3month
1202                  )
1203            )
[105]1204            and (
[118]1205               not $mactimedb{$ip_mac}->{hostname_fq} =~ m/$RE_FLOAT_HOSTNAME/
1206               or $ip_hostname_fq =~ m/$RE_FLOAT_HOSTNAME/
[105]1207               )) {
[74]1208            print "remove ip $ip\n" if $verbose;
1209            delete $computerdb->{$ip};
1210            $database_has_changed++;
1211            }
1212
[108]1213         elsif (
1214            ( $ip_timestamp - $mactimedb{$ip_mac}->{timestamp} > $timestamp_barrier
1215               or (
1216                  $ip_timestamp > $mactimedb{$ip_mac}->{timestamp}
1217                  and $timestamp - $ip_timestamp > $timestamp_3month
1218                  )
1219            )
[105]1220            and (
[118]1221               not $ip_hostname_fq =~ m/$RE_FLOAT_HOSTNAME/
1222               or $mactimedb{$ip_mac}->{hostname_fq} =~ m/$RE_FLOAT_HOSTNAME/
[105]1223               )) {
[104]1224            print "remove ip ".$mactimedb{$ip_mac}->{ip}."\n" if $verbose;
1225            delete $computerdb->{$mactimedb{$ip_mac}->{ip}};
[74]1226            $database_has_changed++;
1227            }
1228
[104]1229         if ( $ip_timestamp > $mactimedb{$ip_mac}->{timestamp}) {
1230            $mactimedb{$ip_mac} = {
1231               ip          => $ip,
1232               timestamp   => $ip_timestamp,
1233               vlan        => $vlan,
1234               hostname_fq => $ip_hostname_fq,
1235               };
[74]1236            }
1237         }
1238      }
1239
[188]1240   if ($repairdns) { # Search and update unkown computer in reverse DNS
[189]1241      LOOP_ON_IP_ADDRESS:
1242      for my $ip (keys %{$computerdb}) {
1243         next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} ne 'unknow';
[188]1244
[189]1245         my $packed_ip = scalar gethostbyname($ip);
1246         next LOOP_ON_IP_ADDRESS if not defined $packed_ip;
[188]1247
1248         my $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET);
[189]1249         next LOOP_ON_IP_ADDRESS if not defined $hostname_fq;
[188]1250
[189]1251         $computerdb->{$ip}{hostname_fq} = $hostname_fq;
[188]1252         $database_has_changed++;
1253         }
1254      }
1255
[74]1256   if ( $database_has_changed ) {
1257      my $dirdb = $KLASK_DB_FILE;
1258         $dirdb =~ s{ / [^/]* $}{}xms;
1259      mkdir "$dirdb", 0755 unless -d "$dirdb";
1260      YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
1261      }
1262   return;
1263   }
1264
[2]1265sub cmd_exportdb {
[113]1266   @ARGV = @_;
[45]1267
1268   my $format = 'txt';
1269
[138]1270   GetOptions(
[45]1271      'format|f=s'  => \$format,
1272      );
1273
1274   my %possible_format = (
1275      txt  => \&cmd_exportdb_txt,
1276      html => \&cmd_exportdb_html,
1277      );
1278
1279   $format = 'txt' if not defined $possible_format{$format};
[63]1280
[45]1281   $possible_format{$format}->(@ARGV);
[63]1282   return;
[45]1283   }
1284
1285sub cmd_exportdb_txt {
[34]1286   test_maindb_environnement();
1287
[151]1288   my $computerdb = computerdb_load();
[2]1289
[82]1290   printf "%-27s %-4s            %-40s %-15s %-18s %-16s %s\n", qw(Switch Port Hostname-FQ IPv4-Address MAC-Address Date VLAN);
[78]1291   print "--------------------------------------------------------------------------------------------------------------------------------------------\n";
[2]1292
1293   LOOP_ON_IP_ADDRESS:
[165]1294   for my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
[63]1295
[2]1296#      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq 'unknow';
1297
1298      # to be improve in the future
1299      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1300
1301# dans le futur
1302#      next if $computerdb->{$ip}{hostname_fq} eq 'unknow';
[63]1303
1304      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
[2]1305      $year += 1900;
1306      $mon++;
[63]1307      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
[2]1308
[177]1309      my $vlan = '';
1310      $vlan = $computerdb->{$ip}{network}.'('.get_current_vlan_id($computerdb->{$ip}{network}).')' if $computerdb->{$ip}{network};
1311
[82]1312      printf "%-28s  %2s  <-------  %-40s %-15s %-18s %-16s %s\n",
[2]1313         $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
[150]1314         $computerdb->{$ip}{switch_port_hr},
[2]1315         $computerdb->{$ip}{hostname_fq},
1316         $ip,
1317         $computerdb->{$ip}{mac_address},
[70]1318         $date,
[177]1319         $vlan;
[2]1320      }
[63]1321   return;
[2]1322   }
1323
[45]1324sub cmd_exportdb_html {
1325   test_maindb_environnement();
1326
[151]1327   my $computerdb = computerdb_load();
[45]1328
1329#<link rel="stylesheet" type="text/css" href="style-klask.css" />
1330#<script src="sorttable-klask.js"></script>
1331
[63]1332   print <<'END_HTML';
[73]1333<table class="sortable" summary="Klask Host Database">
[72]1334 <caption>Klask Host Database</caption>
[45]1335 <thead>
1336  <tr>
[73]1337   <th scope="col" class="klask-header-left">Switch</th>
[45]1338   <th scope="col" class="sorttable_nosort">Port</th>
[164]1339   <th scope="col" class="sorttable_nosort" colspan="2">Link</th>
[73]1340   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
[45]1341   <th scope="col" class="hklask-ipv4">IPv4-Address</th>
[67]1342   <th scope="col" class="sorttable_alpha">MAC-Address</th>
[71]1343   <th scope="col" class="sorttable_alpha">VLAN</th>
[73]1344   <th scope="col" class="klask-header-right">Date</th>
[45]1345  </tr>
1346 </thead>
1347 <tfoot>
1348  <tr>
[73]1349   <th scope="col" class="klask-footer-left">Switch</th>
[45]1350   <th scope="col" class="fklask-port">Port</th>
[163]1351   <th scope="col" class="fklask-link" colspan="2">Link</th>
[73]1352   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
[45]1353   <th scope="col" class="fklask-ipv4">IPv4-Address</th>
1354   <th scope="col" class="fklask-mac">MAC-Address</th>
[71]1355   <th scope="col" class="fklask-vlan">VLAN</th>
[73]1356   <th scope="col" class="klask-footer-right">Date</th>
[45]1357  </tr>
1358 </tfoot>
1359 <tbody>
[63]1360END_HTML
[45]1361
1362   my %mac_count = ();
1363   LOOP_ON_IP_ADDRESS:
[165]1364   for my $ip (keys %{$computerdb}) {
[63]1365
[45]1366      # to be improve in the future
1367      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
[63]1368
[45]1369      $mac_count{$computerdb->{$ip}{mac_address}}++;
1370      }
1371
1372   my $typerow = 'even';
1373
1374   LOOP_ON_IP_ADDRESS:
[165]1375   for my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
[63]1376
[45]1377      # to be improve in the future
1378      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1379
[63]1380      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
[45]1381      $year += 1900;
1382      $mon++;
[63]1383      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
[45]1384
1385#      $odd_or_even++;
1386#      my $typerow = $odd_or_even % 2 ? 'odd' : 'even';
[63]1387      $typerow = $typerow eq 'even' ? 'odd' : 'even';
[45]1388
[163]1389      my $arrow ='&#8592;';
1390         $arrow ='&#8656;' if $computerdb->{$ip}{switch_port_hr} =~ m/^(Trk|Br|Po)/;
1391
[45]1392      my $switch_hostname = $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description} || 'unkown';
1393      chomp $switch_hostname;
[150]1394      my $switch_hostname_sort = sprintf '%s %3s' ,$switch_hostname, $computerdb->{$ip}{switch_port_hr};
[45]1395
[63]1396      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
[45]1397
[63]1398      my $mac_sort = sprintf '%04i-%s', 9999 - $mac_count{$computerdb->{$ip}{mac_address}}, $computerdb->{$ip}{mac_address};
[45]1399
[63]1400      $computerdb->{$ip}{hostname_fq} = 'unknow' if $computerdb->{$ip}{hostname_fq} =~ m/^ \d+ \. \d+ \. \d+ \. \d+ $/xms;
1401      my ( $host_short ) = split m/ \. /xms, $computerdb->{$ip}{hostname_fq};
[45]1402
[178]1403      my $vlan = '';
1404      $vlan = $computerdb->{$ip}{network}.' ('.get_current_vlan_id($computerdb->{$ip}{network}).')' if $computerdb->{$ip}{network};
[71]1405
[63]1406      print <<"END_HTML";
[45]1407  <tr class="$typerow">
1408   <td sorttable_customkey="$switch_hostname_sort">$switch_hostname</td>
[150]1409   <td class="bklask-port">$computerdb->{$ip}{switch_port_hr}</td>
[163]1410   <td colspan="2">$arrow</td>
[45]1411   <td sorttable_customkey="$host_short">$computerdb->{$ip}{hostname_fq}</td>
1412   <td sorttable_customkey="$ip_sort">$ip</td>
1413   <td sorttable_customkey="$mac_sort">$computerdb->{$ip}{mac_address}</td>
[71]1414   <td>$vlan</td>
[45]1415   <td>$date</td>
1416  </tr>
[63]1417END_HTML
[45]1418      }
1419
1420   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1421
1422   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1423   my %db_switch_parent            = %{$switch_connection->{parent}};
1424   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1425   my %db_switch                   = %{$switch_connection->{switch_db}};
1426
1427   for my $sw (sort keys %db_switch_output_port) {
1428
[63]1429      my $switch_hostname_sort = sprintf '%s %3s' ,$sw, $db_switch_output_port{$sw};
[45]1430
[63]1431      $typerow = $typerow eq 'even' ? 'odd' : 'even';
[45]1432
[163]1433      my $arrow ='&#8702;';
1434         $arrow ='&#8680;' if $db_switch_output_port{$sw} =~ m/^(Trk|Br|Po)/;
1435
[45]1436      if (exists $db_switch_parent{$sw}) {
[163]1437         my $mac_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{mac_address};
1438         my $ipv4_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{ipv4_address};
1439         my $timestamp = $db_switch{$db_switch_parent{$sw}->{switch}}->{timestamp};
[45]1440
[163]1441         my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1442         $year += 1900;
1443         $mon++;
1444         my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
[45]1445
[163]1446         my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
[45]1447
[163]1448         my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
[45]1449
[163]1450         my ( $host_short ) = sprintf '%s %3s' , split(m/ \. /xms, $db_switch_parent{$sw}->{switch}, 1), $db_switch_parent{$sw}->{port_hr};
[45]1451
[163]1452         print <<"END_HTML";
[45]1453  <tr class="$typerow">
1454   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
[162]1455   <td class="bklask-port">$db_switch_output_port{$sw}</td>
[164]1456   <td>$arrow</td><td>$db_switch_parent{$sw}->{port_hr}</td>
[162]1457   <td sorttable_customkey="$host_short">$db_switch_parent{$sw}->{switch}</td>
[45]1458   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1459   <td sorttable_customkey="$mac_sort">$mac_address</td>
[71]1460   <td></td>
[45]1461   <td>$date</td>
1462  </tr>
[63]1463END_HTML
[45]1464         }
1465      else {
[63]1466         print <<"END_HTML";
[45]1467  <tr class="$typerow">
1468   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
[162]1469   <td class="bklask-port">$db_switch_output_port{$sw}</td>
[164]1470   <td>$arrow</td><td></td>
[162]1471   <td sorttable_customkey="router">router</td>
[45]1472   <td sorttable_customkey="999999999999"></td>
1473   <td sorttable_customkey="99999"></td>
1474   <td></td>
[71]1475   <td></td>
[45]1476  </tr>
[63]1477END_HTML
[45]1478         }
1479      }
1480
1481   for my $swport (sort keys %db_switch_connected_on_port) {
[63]1482      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[45]1483      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1484
[63]1485         my $switch_hostname_sort = sprintf '%s %3s' ,$sw_connect, $port_connect;
[45]1486
[163]1487         my $mac_address = $db_switch{$sw}->{mac_address};
1488         my $ipv4_address = $db_switch{$sw}->{ipv4_address};
1489         my $timestamp = $db_switch{$sw}->{timestamp};
[45]1490
[163]1491         my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1492         $year += 1900;
1493         $mon++;
1494         my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year,$mon,$mday,$hour,$min;
[45]1495
[163]1496         my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
[45]1497
[163]1498         my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
[45]1499
[163]1500         $typerow = $typerow eq 'even' ? 'odd' : 'even';
[45]1501
[163]1502         my $arrow ='&#8701;';
1503            $arrow ='&#8678;' if $port_connect =~ m/^(Trk|Br|Po)/;
1504
[45]1505         if (exists $db_switch_output_port{$sw}) {
1506
[63]1507            my ( $host_short ) = sprintf '%s %3s' , split( m/\./xms, $sw, 1), $db_switch_output_port{$sw};
[45]1508
[63]1509            print <<"END_HTML";
[45]1510  <tr class="$typerow">
1511   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
[162]1512   <td class="bklask-port">$port_connect</td>
[163]1513   <td>$arrow</td><td>$db_switch_output_port{$sw}</td>
[162]1514   <td sorttable_customkey="$host_short">$sw</td>
[45]1515   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1516   <td sorttable_customkey="$mac_sort">$mac_address</td>
[71]1517   <td></td>
[45]1518   <td>$date</td>
1519  </tr>
[63]1520END_HTML
[45]1521            }
1522         else {
[63]1523            print <<"END_HTML";
[45]1524  <tr class="$typerow">
1525   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
[162]1526   <td class="bklask-port">$port_connect</td>
[163]1527   <td>$arrow</td><td></td>
[162]1528   <td sorttable_customkey="$sw">$sw</td>
[45]1529   <td sorttable_customkey="">$ipv4_address</td>
1530   <td sorttable_customkey="">$mac_address</td>
[71]1531   <td></td>
[45]1532   <td>$date</td>
1533  </tr>
[63]1534END_HTML
[45]1535            }
1536         }
1537      }
1538
[63]1539   print <<'END_HTML';
[45]1540 </tbody>
1541</table>
[63]1542END_HTML
1543   return;
[45]1544   }
1545
[133]1546sub cmd_bad_vlan_id {
[114]1547   test_maindb_environnement();
1548
[151]1549   my $computerdb = computerdb_load();
[114]1550
[132]1551   # create a database with the most recent computer by switch port
[114]1552   my %swithportdb = ();
1553   LOOP_ON_IP_ADDRESS:
[165]1554   for my $ip (keys %{$computerdb}) {
[114]1555      # to be improve in the future
1556      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
[132]1557      next LOOP_ON_IP_ADDRESS if ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}) eq 'unknow';
1558      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{switch_port} eq '0';
[114]1559
1560      my $ip_timestamp   = $computerdb->{$ip}{timestamp};
1561      my $ip_mac         = $computerdb->{$ip}{mac_address};
1562      my $ip_hostname_fq = $computerdb->{$ip}{hostname_fq};
1563
[115]1564      my $swpt = sprintf "%-28s  %2s",
1565         $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
[150]1566         $computerdb->{$ip}{switch_port_hr};
[114]1567      $swithportdb{$swpt} ||= {
1568         ip          => $ip,
1569         timestamp   => $ip_timestamp,
1570         vlan        => $computerdb->{$ip}{network},
1571         hostname_fq => $ip_hostname_fq,
1572         mac_address => $ip_mac,
1573         };
[117]1574
[132]1575      # if float computer, set date 15 day before warning...
1576      my $ip_timestamp_mod = $ip_timestamp;
1577      my $ip_timestamp_ref = $swithportdb{$swpt}->{timestamp};
1578      $ip_timestamp_mod -= 15 * 24 * 3600 if $ip_hostname_fq =~ m/$RE_FLOAT_HOSTNAME/;
1579      $ip_timestamp_ref -= 15 * 24 * 3600 if $swithportdb{$swpt}->{hostname_fq} =~ m/$RE_FLOAT_HOSTNAME/;
[198]1580
[132]1581      if ($ip_timestamp_mod > $ip_timestamp_ref) {
[114]1582         $swithportdb{$swpt} = {
1583            ip          => $ip,
1584            timestamp   => $ip_timestamp,
1585            vlan        => $computerdb->{$ip}{network},
1586            hostname_fq => $ip_hostname_fq,
1587            mac_address => $ip_mac,
1588            };
1589         }
1590      }
1591
[133]1592   LOOP_ON_RECENT_COMPUTER:
[165]1593   for my $swpt (keys %swithportdb) {
[133]1594      next LOOP_ON_RECENT_COMPUTER if $swpt =~ m/^\s*0$/;
1595      next LOOP_ON_RECENT_COMPUTER if $swithportdb{$swpt}->{hostname_fq} !~ m/$RE_FLOAT_HOSTNAME/;
[117]1596
[114]1597      my $src_ip = $swithportdb{$swpt}->{ip};
1598      my $src_timestamp = 0;
[133]1599      LOOP_ON_IP_ADDRESS:
[165]1600      for my $ip (keys %{$computerdb}) {
[133]1601         next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{mac_address} ne  $swithportdb{$swpt}->{mac_address};
1602         next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} =~ m/$RE_FLOAT_HOSTNAME/;
1603         next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{timestamp} < $src_timestamp;
[198]1604
[114]1605         $src_ip = $ip;
1606         $src_timestamp = $computerdb->{$ip}{timestamp};
1607         }
[132]1608
1609      # keep only if float computer is the most recent
[133]1610      next LOOP_ON_RECENT_COMPUTER if $src_timestamp == 0;
1611      next LOOP_ON_RECENT_COMPUTER if $swithportdb{$swpt}->{timestamp} < $src_timestamp;
[132]1612
[114]1613      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $swithportdb{$swpt}->{timestamp};
1614      $year += 1900;
1615      $mon++;
[132]1616      my $date = sprintf '%04i-%02i-%02i/%02i:%02i', $year, $mon, $mday, $hour, $min;
[114]1617
1618      ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$src_ip}{timestamp};
1619      $year += 1900;
1620      $mon++;
[132]1621      my $src_date = sprintf '%04i-%02i-%02i/%02i:%02i', $year, $mon, $mday, $hour, $min;
[114]1622
[176]1623      my $vlan_id = get_current_vlan_id($computerdb->{$src_ip}{network});
1624
1625      printf "%s / %-10s +-> %-10s(%i)  %s %s %s %s\n",
1626         $swpt, $swithportdb{$swpt}->{vlan}, $computerdb->{$src_ip}{network}, $vlan_id,
[114]1627         $date,
1628         $src_date,
[132]1629         $computerdb->{$src_ip}{mac_address},
[114]1630         $computerdb->{$src_ip}{hostname_fq};
1631      }
1632   }
1633
[129]1634sub cmd_set_vlan_port {
1635   my $switch_name = shift || q{};
1636   my $mac_address = shift || q{};
1637
1638   if ($switch_name eq q{} or $mac_address eq q{}) {
1639      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
1640      }
1641
[196]1642   $switch_name = join(',', map {$_->{hostname}} @SWITCH_LIST ) if $switch_name eq q{*};
[129]1643
1644   for my $sw_name (split /,/, $switch_name) {
1645      if (not defined $SWITCH_DB{$sw_name}) {
1646         die "Switch $sw_name must be defined in klask configuration file\n";
1647         }
1648
1649      my $sw = $SWITCH_DB{$sw_name};
1650      my %session = ( -hostname => $sw->{hostname} );
1651         $session{-version} = $sw->{version}   || 1;
1652         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1653      if (exists $sw->{version} and $sw->{version} eq '3') {
1654         $session{-username} = $sw->{username} || 'snmpadmin';
1655         }
1656      else {
1657         $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1658         }
1659
1660      my $research1 = $OID_NUMBER{searchPort1} . mac_address_hex_to_dec($mac_address);
[144]1661      my $research2 = $OID_NUMBER{searchPort2} .'.'. 0 . mac_address_hex_to_dec($mac_address);
[129]1662      print "Klask search OID $research1 on switch $sw_name\n";
1663      print "Klask search OID $research2 on switch $sw_name\n";
1664
1665      my ($session, $error) = Net::SNMP->session( %session );
1666      print "$error \n" if $error;
1667
1668      my $result = $session->get_request(
1669         -varbindlist => [$research1]
1670         );
1671      if (not defined $result) {
1672         $result = $session->get_request(
1673            -varbindlist => [$research2]
1674            );
1675         $result->{$research1} = $result->{$research2} if defined $result;
1676         }
1677
1678      if (defined $result and $result->{$research1} ne 'noSuchInstance') {
1679         my $swport = $result->{$research1};
1680         print "Klask find MAC $mac_address on switch $sw_name port $swport\n";
1681         }
1682      else {
1683         print "Klask do not find MAC $mac_address on switch $sw_name\n";
1684         }
1685
1686      $session->close;
1687      }
1688   return;
1689   }
1690
1691sub cmd_get_vlan_port {
[141]1692   @ARGV = @_;
1693
1694   my $verbose;
1695   GetOptions(
1696      'verbose|v' => \$verbose,
1697      );
1698
1699   my $switch_name = shift @ARGV || q{};
1700   my $switch_port = shift @ARGV || q{};
1701
1702   if ($switch_name eq q{} or $switch_port eq q{}) {
1703      die "Usage: klask get-vlan-port SWITCH_NAME PORT\n";
1704      }
1705
1706   for my $sw_name (split /,/, $switch_name) {
1707      if (not defined $SWITCH_DB{$sw_name}) {
1708         die "Switch $sw_name must be defined in klask configuration file\n";
1709         }
1710
1711      my $sw = $SWITCH_DB{$sw_name};
1712      my %session = ( -hostname => $sw->{hostname} );
1713         $session{-version} = $sw->{version}   || 1;
1714         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1715      if (exists $sw->{version} and $sw->{version} eq '3') {
1716         $session{-username} = $sw->{username} || 'snmpadmin';
1717         }
1718      else {
1719         $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1720         }
1721
[142]1722      my $search = $OID_NUMBER{'vlanPortDefault'} . ".$switch_port";
[141]1723
1724      my ($session, $error) = Net::SNMP->session( %session );
1725      print "$error \n" if $error;
1726
1727      my $result = $session->get_request(
1728         -varbindlist => [$search],
1729         );
1730
1731      if (defined $result and $result->{$search} ne 'noSuchInstance') {
1732         my $vlan_id = $result->{$search} || 'empty';
1733         print "Klask VLAN Id $vlan_id on switch $sw_name on port $switch_port\n";
1734         }
1735      else {
[142]1736         print "Klask do not find VLAN Id on switch $sw_name on port $switch_port\n";
[141]1737         }
1738
1739      $session->close;
1740      }
1741   return;
[129]1742   }
1743
1744sub cmd_set_vlan_name {
1745   }
1746
[141]1747# snmpset -v 1 -c public sw1-batG0-legi.hmg.priv "$OID_NUMBER{'hpicfReset'}.0" i 2;
1748sub cmd_rebootsw {
1749   @ARGV = @_;
1750
1751   my $verbose;
1752   GetOptions(
1753      'verbose|v' => \$verbose,
1754      );
1755
1756   my $switch_name = shift @ARGV || q{};
1757
[142]1758   if ($switch_name eq q{}) {
[141]1759      die "Usage: klask rebootsw SWITCH_NAME\n";
1760      }
1761
1762   for my $sw_name (split /,/, $switch_name) {
1763      if (not defined $SWITCH_DB{$sw_name}) {
1764         die "Switch $sw_name must be defined in klask configuration file\n";
1765         }
1766
1767      my $sw = $SWITCH_DB{$sw_name};
1768      my %session = ( -hostname => $sw->{hostname} );
1769         $session{-version} = $sw->{version}   || 1;
1770         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1771      if (exists $sw->{version} and $sw->{version} eq '3') {
1772         $session{-username} = $sw->{username} || 'snmpadmin';
1773         }
1774      else {
1775         $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1776         }
1777
1778      my ($session, $error) = Net::SNMP->session( %session );
1779      print "$error \n" if $error;
1780
1781      my $result = $session->set_request(
1782         -varbindlist => ["$OID_NUMBER{'hpicfReset'}.0", INTEGER, 2],
1783         );
1784
1785      $session->close;
1786      }
1787   return;
1788   }
1789
[129]1790sub cmd_get_vlan_name {
1791   my $switch_name = shift || q{};
1792   my $vlan_id     = shift || q{};
1793
1794   if ($switch_name eq q{} or $vlan_id eq q{}) {
1795      die "Usage: klask get-vlan-name SWITCH_NAME VLAN_ID\n";
1796      }
1797
[196]1798   $switch_name = join(',', map {$_->{hostname}} @SWITCH_LIST ) if $switch_name eq q{*};
[129]1799
1800   for my $sw_name (split /,/, $switch_name) {
1801      if (not defined $SWITCH_DB{$sw_name}) {
1802         die "Switch $sw_name must be defined in klask configuration file\n";
1803         }
1804
1805      my $sw = $SWITCH_DB{$sw_name};
1806      my %session = ( -hostname => $sw->{hostname} );
1807         $session{-version} = $sw->{version}   || 1;
1808         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1809      if (exists $sw->{version} and $sw->{version} eq '3') {
1810         $session{-username} = $sw->{username} || 'snmpadmin';
1811         }
1812      else {
1813         $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1814         }
1815
[130]1816      my $search_vlan_name = $OID_NUMBER{vlanName} . ".$vlan_id";
[129]1817
1818      my ($session, $error) = Net::SNMP->session( %session );
1819      print "$error \n" if $error;
1820
1821      my $result = $session->get_request(
1822         -varbindlist => [$search_vlan_name]
1823         );
1824
1825      if (defined $result and $result->{$search_vlan_name} ne 'noSuchInstance') {
[131]1826         my $vlan_name = $result->{$search_vlan_name} || 'empty';
[130]1827         print "Klask find VLAN $vlan_id on switch $sw_name with name $vlan_name\n";
[129]1828         }
1829      else {
1830         print "Klask do not find VLAN $vlan_id on switch $sw_name\n";
1831         }
1832
1833      $session->close;
1834      }
1835   return;
1836   }
1837
[111]1838sub cmd_ip_location {
[151]1839   my $computerdb = computerdb_load();
[2]1840
1841   LOOP_ON_IP_ADDRESS:
[165]1842   for my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
[2]1843
1844      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1845
[63]1846      my $sw_hostname = $computerdb->{$ip}{switch_hostname} || q{};
[133]1847      next LOOP_ON_IP_ADDRESS if $sw_hostname eq 'unknow';
[63]1848
1849      my $sw_location = q{};
[133]1850      LOOP_ON_ALL_SWITCH:
[196]1851      for my $sw (@SWITCH_LIST) {
[133]1852         next LOOP_ON_ALL_SWITCH if $sw_hostname ne $sw->{hostname};
[2]1853         $sw_location = $sw->{location};
1854         last;
1855         }
1856
[63]1857      printf "%s: \"%s\"\n", $ip, $sw_location if not $sw_location eq q{};
[2]1858      }
[63]1859   return;
[2]1860   }
1861
[69]1862sub cmd_ip_free {
[113]1863   @ARGV = @_; # VLAN name with option
[69]1864
1865   my $days_to_dead = 365 * 2;
1866   my $format = 'txt';
[97]1867   my $verbose;
[69]1868
[138]1869   GetOptions(
[69]1870      'day|d=i'      => \$days_to_dead,
1871      'format|f=s'   => \$format,
[97]1872      'verbose|v'    => \$verbose,
[69]1873      );
1874
[72]1875   my %possible_format = (
1876      txt  => \&cmd_ip_free_txt,
1877      html => \&cmd_ip_free_html,
[97]1878      none => sub {},
[72]1879      );
1880   $format = 'txt' if not defined $possible_format{$format};
1881
[110]1882   my @vlan_name = @ARGV;
[69]1883   @vlan_name = get_list_network() if not @vlan_name;
1884
1885   my $computerdb = {};
[151]1886      $computerdb = computerdb_load() if -e "$KLASK_DB_FILE";
[69]1887   my $timestamp = time;
1888
1889   my $timestamp_barrier = $timestamp - (3600 * 24 * $days_to_dead );
1890
[72]1891   my %result_ip = ();
[69]1892
1893   ALL_NETWORK:
1894   for my $vlan (@vlan_name) {
1895
1896      my @ip_list = get_list_ip($vlan);
[97]1897
[69]1898      LOOP_ON_IP_ADDRESS:
1899      for my $ip (@ip_list) {
1900
[135]1901         if (exists $computerdb->{$ip}) {
1902            next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{timestamp} > $timestamp_barrier;
[198]1903
[135]1904            my $mac_address = $computerdb->{$ip}{mac_address};
1905            LOOP_ON_DATABASE:
[165]1906            for my $ip_db (keys %{$computerdb}) {
[135]1907               next LOOP_ON_DATABASE if $computerdb->{$ip_db}{mac_address} ne $mac_address;
1908               next LOOP_ON_IP_ADDRESS if $computerdb->{$ip_db}{timestamp} > $timestamp_barrier;
1909               }
1910            }
[69]1911
1912         my $ip_date_last_detection = '';
1913         if (exists $computerdb->{$ip}) {
1914            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1915            $year += 1900;
1916            $mon++;
1917            $ip_date_last_detection = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1918            }
1919
1920         my $packed_ip = scalar gethostbyname($ip);
1921         my $hostname_fq = 'unknown';
[186]1922            $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) || 'unknown' if defined $packed_ip and get_current_scan_mode($vlan) eq 'active';
[135]1923
1924         next LOOP_ON_IP_ADDRESS if $hostname_fq =~ m/$RE_FLOAT_HOSTNAME/;
1925
1926         $result_ip{$ip} ||= {};
1927         $result_ip{$ip}->{date_last_detection} = $ip_date_last_detection;
[69]1928         $result_ip{$ip}->{hostname_fq} = $hostname_fq;
1929         $result_ip{$ip}->{vlan} = $vlan;
[97]1930
1931         printf "VERBOSE_1: %-15s %-12s %s\n", $ip, $vlan, $hostname_fq if $verbose;
[69]1932         }
1933      }
1934
[72]1935   $possible_format{$format}->(%result_ip);
1936   }
1937
1938sub cmd_ip_free_txt {
1939   my %result_ip = @_;
[198]1940
[69]1941   printf "%-15s %-40s %-16s %s\n", qw(IPv4-Address Hostname-FQ Date VLAN);
1942   print "-------------------------------------------------------------------------------\n";
[72]1943   LOOP_ON_IP_ADDRESS:
[165]1944   for my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
[180]1945         my $vlan_nameid = $result_ip{$ip}->{vlan}.'('.get_current_vlan_id($result_ip{$ip}->{vlan}).')';
1946         printf "%-15s %-40s %-16s %s\n", $ip, $result_ip{$ip}->{hostname_fq}, $result_ip{$ip}->{date_last_detection}, $vlan_nameid;
[69]1947      }
1948   }
1949
[72]1950sub cmd_ip_free_html {
1951   my %result_ip = @_;
1952
1953   print <<'END_HTML';
[73]1954<table class="sortable" summary="Klask Free IP Database">
1955 <caption>Klask Free IP Database</caption>
[72]1956 <thead>
1957  <tr>
[73]1958   <th scope="col" class="klask-header-left">IPv4-Address</th>
[72]1959   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
1960   <th scope="col" class="sorttable_alpha">VLAN</th>
[73]1961   <th scope="col" class="klask-header-right">Date</th>
[72]1962  </tr>
1963 </thead>
1964 <tfoot>
1965  <tr>
[73]1966   <th scope="col" class="klask-footer-left">IPv4-Address</th>
1967   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
[72]1968   <th scope="col" class="fklask-vlan">VLAN</th>
[73]1969   <th scope="col" class="klask-footer-right">Date</th>
[72]1970  </tr>
1971 </tfoot>
1972 <tbody>
1973END_HTML
1974
1975   my $typerow = 'even';
1976
1977   LOOP_ON_IP_ADDRESS:
[165]1978   for my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
[72]1979
1980      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1981
1982      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1983      my ( $host_short ) = split m/ \. /xms, $result_ip{$ip}->{hostname_fq};
1984
[180]1985      my $vlan_nameid = $result_ip{$ip}->{vlan}.'('.get_current_vlan_id($result_ip{$ip}->{vlan}).')';
1986
[72]1987      print <<"END_HTML";
1988  <tr class="$typerow">
1989   <td sorttable_customkey="$ip_sort">$ip</td>
1990   <td sorttable_customkey="$host_short">$result_ip{$ip}->{hostname_fq}</td>
[180]1991   <td>$vlan_nameid</td>
[72]1992   <td>$result_ip{$ip}->{date_last_detection}</td>
1993  </tr>
1994END_HTML
1995      }
1996   print <<'END_HTML';
1997 </tbody>
1998</table>
1999END_HTML
2000   }
2001
[2]2002sub cmd_enable {
2003   my $switch = shift;
2004   my $port   = shift;
[63]2005
2006   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
2007   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
[2]2008   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
[63]2009   return;
[2]2010   }
2011
2012sub cmd_disable {
2013   my $switch = shift;
2014   my $port   = shift;
[63]2015
[2]2016   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
[63]2017   return;
[2]2018   }
2019
2020sub cmd_status {
2021   my $switch = shift;
2022   my $port   = shift;
[63]2023
[2]2024   system "snmpget -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port";
[63]2025   return;
[2]2026   }
2027
[35]2028sub cmd_search_mac_on_switch {
[138]2029   @ARGV = @_;
[63]2030
[138]2031   my $verbose;
[144]2032   my $vlan_id = 0;
[138]2033
2034   GetOptions(
2035      'verbose|v' => \$verbose,
[144]2036      'vlan|l=i'  => \$vlan_id,
[138]2037      );
2038
2039   my $switch_name = shift @ARGV || q{};
2040   my $mac_address = shift @ARGV || q{};
2041
[63]2042   if ($switch_name eq q{} or $mac_address eq q{}) {
[39]2043      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
[38]2044      }
[39]2045
[138]2046   $mac_address = normalize_mac_address($mac_address);
[196]2047   $switch_name = join(',', map {$_->{hostname}} @SWITCH_LIST ) if $switch_name eq q{*};
[39]2048
[112]2049   for my $sw_name (split /,/, $switch_name) {
2050      if (not defined $SWITCH_DB{$sw_name}) {
2051         die "Switch $sw_name must be defined in klask configuration file\n";
2052         }
[39]2053
[112]2054      my $sw = $SWITCH_DB{$sw_name};
2055      my %session = ( -hostname => $sw->{hostname} );
2056         $session{-version} = $sw->{version}   || 1;
2057         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
2058      if (exists $sw->{version} and $sw->{version} eq '3') {
2059         $session{-username} = $sw->{username} || 'snmpadmin';
2060         }
2061      else {
2062         $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
2063         }
[2]2064
[121]2065      my $research1 = $OID_NUMBER{searchPort1} . mac_address_hex_to_dec($mac_address);
[144]2066      my $research2 = $OID_NUMBER{searchPort2} .'.'. $vlan_id . mac_address_hex_to_dec($mac_address);
[138]2067      print "Klask search OID $research1 on switch $sw_name\n" if $verbose;
2068      print "Klask search OID $research2 on switch $sw_name\n" if $verbose;
[35]2069
[112]2070      my ($session, $error) = Net::SNMP->session( %session );
2071      print "$error \n" if $error;
[63]2072
[112]2073      my $result = $session->get_request(
[124]2074         -varbindlist => [$research1]
[112]2075         );
[124]2076      if (not defined $result) {
2077         $result = $session->get_request(
2078            -varbindlist => [$research2]
2079            );
2080         $result->{$research1} = $result->{$research2} if defined $result;
2081         }
[112]2082
[124]2083      if (defined $result and $result->{$research1} ne 'noSuchInstance') {
[181]2084         my $swport_num = $result->{$research1};
2085         my $swport_hr = get_human_readable_port($sw->{model}, snmp_get_swithport_hr($session, $swport_num));
2086         print "Klask find MAC $mac_address on switch $sw_name port $swport_hr\n";
[123]2087         }
2088      else {
[138]2089         print "Klask do not find MAC $mac_address on switch $sw_name\n" if $verbose;
[112]2090         }
2091
[35]2092      $session->close;
2093      }
[63]2094   return;
[35]2095   }
2096
[4]2097sub cmd_updatesw {
[113]2098   @ARGV = @_;
[2]2099
[79]2100   my $verbose;
2101
[138]2102   GetOptions(
[79]2103      'verbose|v' => \$verbose,
2104      );
2105
[4]2106   init_switch_names('yes');    #nomme les switchs
[2]2107   print "\n";
2108
2109   my %where = ();
2110   my %db_switch_output_port = ();
[83]2111   my %db_switch_ip_hostnamefq = ();
[2]2112
2113   DETECT_ALL_ROUTER:
[11]2114   for my $one_router ( get_list_main_router(get_list_network()) ) {
[196]2115      print "Info: router loop $one_router\n" if $verbose;
[63]2116      my %resol_arp = resolve_ip_arp_host($one_router, q{*}, q{low}); # resolution arp
[83]2117
[2]2118      next DETECT_ALL_ROUTER if $resol_arp{mac_address} eq 'unknow';
[83]2119      print "VERBOSE_1: Router detected $resol_arp{ipv4_address} - $resol_arp{mac_address}\n" if $verbose;
2120
[144]2121      my $vlan_name = get_current_vlan_name_for_interface($resol_arp{interface});
2122      my $vlan_id   = get_current_vlan_id($vlan_name);
2123      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address},$vlan_id); # retrouve les emplacements des routeurs
[2]2124      }
2125
2126   ALL_ROUTER_IP_ADDRESS:
[83]2127   for my $ip_router (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
[63]2128
[83]2129      next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip_router}; # /a priori/ idiot car ne sers à rien...
[2]2130
2131      ALL_SWITCH_CONNECTED:
[83]2132      for my $switch_detected ( keys %{$where{$ip_router}} ) {
[2]2133
[83]2134         my $switch = $where{$ip_router}->{$switch_detected};
[2]2135
2136         next ALL_SWITCH_CONNECTED if $switch->{port} eq '0';
[63]2137
[154]2138         $db_switch_output_port{$switch->{hostname}} = $switch->{port_hr};
2139         print "VERBOSE_2: output port $switch->{hostname} : $switch->{port_hr}\n" if $verbose;
[2]2140         }
[63]2141      }
[2]2142
2143   my %db_switch_link_with = ();
2144
[83]2145   my @list_all_switch = ();
[2]2146   my @list_switch_ipv4 = ();
[196]2147   for my $sw (@SWITCH_LIST){
[83]2148      push @list_all_switch, $sw->{hostname};
[2]2149      }
2150
[45]2151   my $timestamp = time;
2152
[2]2153   ALL_SWITCH:
[198]2154   for my $one_switch (@list_all_switch) {
2155      my %resol_arp = resolve_ip_arp_host($one_switch, q{*}, q{low}); # arp resolution
[199]2156      if (exists $SWITCH_DB{$one_switch}{'fake-ip'}) {
2157         print "WARNING: fake ip on switch $one_switch -> $SWITCH_DB{$one_switch}{'fake-ip'} / $resol_arp{ipv4_address}\n" if $verbose;
2158         my %resol_arp_alt = resolve_ip_arp_host($SWITCH_DB{$one_switch}{'fake-ip'}, q{*}, q{low}); # arp resolution
2159         if ($resol_arp_alt{mac_address} ne 'unknow') {
2160            $resol_arp{mac_address}   = $resol_arp_alt{mac_address};
2161            $resol_arp{interface}     = $resol_arp_alt{interface};
2162            $resol_arp{ipv4_address} .= '*';
2163            }
2164         }
[198]2165      print "Info: switch loop $one_switch\n" if $verbose;
[2]2166      next ALL_SWITCH if $resol_arp{mac_address} eq 'unknow';
[63]2167
[82]2168      push @list_switch_ipv4, $resol_arp{ipv4_address};
[63]2169
[144]2170      my $vlan_name = get_current_vlan_name_for_interface($resol_arp{interface});
2171      my $vlan_id   = get_current_vlan_id($vlan_name);
2172      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address},$vlan_id); # find port on all switch
[2]2173
[80]2174      if ($verbose) {
[198]2175         print "VERBOSE_3: $one_switch $resol_arp{ipv4_address} $resol_arp{mac_address}\n";
2176         print "VERBOSE_3: $one_switch --- ",
[82]2177            join(' + ', keys %{$where{$resol_arp{ipv4_address}}}),
2178            "\n";
[80]2179         }
2180
[83]2181      $db_switch_ip_hostnamefq{$resol_arp{ipv4_address}} = $resol_arp{hostname_fq};
2182      print "VERBOSE_4: db_switch_ip_hostnamefq $resol_arp{ipv4_address} -> $resol_arp{hostname_fq}\n" if $verbose;
[45]2183
[198]2184      $SWITCH_DB{$one_switch}->{ipv4_address} = $resol_arp{ipv4_address};
2185      $SWITCH_DB{$one_switch}->{mac_address}  = $resol_arp{mac_address};
2186      $SWITCH_DB{$one_switch}->{timestamp}    = $timestamp;
[2]2187      }
[63]2188
[2]2189   ALL_SWITCH_IP_ADDRESS:
[199]2190   for my $ip (@list_switch_ipv4) {
2191#   for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
[63]2192
[83]2193      print "VERBOSE_5: loop on $db_switch_ip_hostnamefq{$ip}\n" if $verbose;
2194
[2]2195      next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
[84]2196#      next ALL_SWITCH_IP_ADDRESS if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostnamefq{$ip} };
[2]2197
2198      DETECTED_SWITCH:
2199      for my $switch_detected ( keys %{$where{$ip}} ) {
2200
2201         my $switch = $where{$ip}->{$switch_detected};
[154]2202         print "VERBOSE_6: $db_switch_ip_hostnamefq{$ip} -> $switch->{hostname} : $switch->{port_hr}\n" if $verbose;
[2]2203
2204         next if $switch->{port}     eq '0';
[154]2205         next if $switch->{port_hr}  eq $db_switch_output_port{$switch->{hostname}};
[83]2206         next if $switch->{hostname} eq $db_switch_ip_hostnamefq{$ip}; # $computerdb->{$ip}{hostname};
[2]2207
[83]2208         $db_switch_link_with{ $db_switch_ip_hostnamefq{$ip} } ||= {};
[160]2209         $db_switch_link_with{ $db_switch_ip_hostnamefq{$ip} }->{ $switch->{hostname} } = $switch->{port_hr};
[83]2210         print "VERBOSE_7: +++++\n" if $verbose;
[2]2211         }
2212
2213      }
[63]2214
[2]2215   my %db_switch_connected_on_port = ();
2216   my $maybe_more_than_one_switch_connected = 'yes';
[181]2217   my $cloop = 0;
[63]2218
[181]2219   while ($maybe_more_than_one_switch_connected eq 'yes' and $cloop < 100) {
2220      $cloop++;
2221      print "VERBOSE_9: cloop reduction step: $cloop\n" if $verbose;
[2]2222      for my $sw (keys %db_switch_link_with) {
2223         for my $connect (keys %{$db_switch_link_with{$sw}}) {
[63]2224
[160]2225            my $port_hr = $db_switch_link_with{$sw}->{$connect};
[63]2226
[160]2227            $db_switch_connected_on_port{"$connect:$port_hr"} ||= {};
2228            $db_switch_connected_on_port{"$connect:$port_hr"}->{$sw}++; # Just to define the key
[2]2229            }
2230         }
2231
2232      $maybe_more_than_one_switch_connected  = 'no';
2233
2234      SWITCH_AND_PORT:
2235      for my $swport (keys %db_switch_connected_on_port) {
[63]2236
[2]2237         next if keys %{$db_switch_connected_on_port{$swport}} == 1;
[63]2238
[2]2239         $maybe_more_than_one_switch_connected = 'yes';
2240
[63]2241         my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]2242         my @sw_on_same_port = keys %{$db_switch_connected_on_port{$swport}};
[181]2243         print "VERBOSE_10: $swport -- ".$#sw_on_same_port." -- @sw_on_same_port\n" if $verbose;
[2]2244
2245         CONNECTED:
2246         for my $sw_connected (@sw_on_same_port) {
[63]2247
[2]2248            next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
[63]2249
[2]2250            $db_switch_connected_on_port{$swport} = {$sw_connected => 1};
[63]2251
[2]2252            for my $other_sw (@sw_on_same_port) {
2253               next if $other_sw eq $sw_connected;
[63]2254
[2]2255               delete $db_switch_link_with{$other_sw}->{$sw_connect};
2256               }
[63]2257
[2]2258            # We can not do better for this switch for this loop
2259            next SWITCH_AND_PORT;
2260            }
2261         }
2262      }
2263
2264   my %db_switch_parent =();
2265
2266   for my $sw (keys %db_switch_link_with) {
2267      for my $connect (keys %{$db_switch_link_with{$sw}}) {
[63]2268
[160]2269         my $port_hr = $db_switch_link_with{$sw}->{$connect};
[63]2270
[160]2271         $db_switch_connected_on_port{"$connect:$port_hr"} ||= {};
2272         $db_switch_connected_on_port{"$connect:$port_hr"}->{$sw} = $port_hr;
[63]2273
[161]2274         $db_switch_parent{$sw} = {switch => $connect, port_hr => $port_hr};
[2]2275         }
2276      }
2277
[63]2278   print "Switch output port and parent port connection\n";
[2]2279   print "---------------------------------------------\n";
2280   for my $sw (sort keys %db_switch_output_port) {
2281      if (exists $db_switch_parent{$sw}) {
[154]2282         printf "%-28s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port_hr}, $db_switch_parent{$sw}->{switch};
[2]2283         }
2284      else {
[82]2285         printf "%-28s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
[2]2286         }
2287      }
2288   print "\n";
2289
2290   print "Switch parent and children port inter-connection\n";
2291   print "------------------------------------------------\n";
[63]2292   for my $swport (sort keys %db_switch_connected_on_port) {
2293      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]2294      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
2295         if (exists $db_switch_output_port{$sw}) {
[82]2296            printf "%-28s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
[2]2297            }
2298         else {
[82]2299            printf "%-28s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
[2]2300            }
2301         }
2302      }
2303
2304   my $switch_connection = {
2305      output_port       => \%db_switch_output_port,
2306      parent            => \%db_switch_parent,
2307      connected_on_port => \%db_switch_connected_on_port,
2308      link_with         => \%db_switch_link_with,
[25]2309      switch_db         => \%SWITCH_DB,
[2]2310      };
[63]2311
[44]2312   YAML::Syck::DumpFile("$KLASK_SW_FILE", $switch_connection);
[63]2313   return;
[2]2314   }
2315
[4]2316sub cmd_exportsw {
[113]2317   @ARGV = @_;
[2]2318
[34]2319   test_switchdb_environnement();
2320
[4]2321   my $format = 'txt';
2322
[138]2323   GetOptions(
[4]2324      'format|f=s'  => \$format,
2325      );
2326
2327   my %possible_format = (
2328      txt => \&cmd_exportsw_txt,
2329      dot => \&cmd_exportsw_dot,
2330      );
2331
2332   $format = 'txt' if not defined $possible_format{$format};
[63]2333
[4]2334   $possible_format{$format}->(@ARGV);
[63]2335   return;
[4]2336   }
2337
2338sub cmd_exportsw_txt {
2339
[44]2340   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
[4]2341
2342   my %db_switch_output_port       = %{$switch_connection->{output_port}};
2343   my %db_switch_parent            = %{$switch_connection->{parent}};
2344   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
2345
[63]2346   print "Switch output port and parent port connection\n";
[4]2347   print "---------------------------------------------\n";
2348   for my $sw (sort keys %db_switch_output_port) {
2349      if (exists $db_switch_parent{$sw}) {
[154]2350         printf "%-28s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port_hr}, $db_switch_parent{$sw}->{switch};
[4]2351         }
2352      else {
[82]2353         printf "%-28s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
[4]2354         }
2355      }
2356   print "\n";
2357
2358   print "Switch parent and children port inter-connection\n";
2359   print "------------------------------------------------\n";
[63]2360   for my $swport (sort keys %db_switch_connected_on_port) {
2361      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[4]2362      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
2363         if (exists $db_switch_output_port{$sw}) {
[82]2364            printf "%-28s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
[4]2365            }
2366         else {
[82]2367            printf "%-28s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
[4]2368            }
2369         }
2370      }
[63]2371   return;
[4]2372   }
2373
2374sub cmd_exportsw_dot {
2375
[44]2376   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
[63]2377
[2]2378   my %db_switch_output_port       = %{$switch_connection->{output_port}};
2379   my %db_switch_parent            = %{$switch_connection->{parent}};
2380   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
2381   my %db_switch_link_with         = %{$switch_connection->{link_with}};
[25]2382   my %db_switch_global            = %{$switch_connection->{switch_db}};
2383
[2]2384   my %db_building= ();
[196]2385   for my $sw (@SWITCH_LIST) {
[63]2386      my ($building, $location) = split m/ \/ /xms, $sw->{location}, 2;
[2]2387      $db_building{$building} ||= {};
2388      $db_building{$building}->{$location} ||= {};
2389      $db_building{$building}->{$location}{ $sw->{hostname} } = 'y';
2390      }
[63]2391
2392
[2]2393   print "digraph G {\n";
[132]2394   print "rankdir = LR;\n";
[2]2395
[4]2396   print "site [label = \"site\", color = black, fillcolor = gold, shape = invhouse, style = filled];\n";
2397   print "internet [label = \"internet\", color = black, fillcolor = cyan, shape = house, style = filled];\n";
[2]2398
[202]2399   my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime time;
2400   $year += 1900;
2401   $mon++;
2402   my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
2403   print "\"$date\" [ color = white, fillcolor = black, shape = polygon, sides=14, style = filled, fontcolor = white ]\n";
[201]2404
[2]2405   my $b=0;
2406   for my $building (keys %db_building) {
2407      $b++;
[63]2408
[4]2409      print "\"building$b\" [label = \"$building\", color = black, fillcolor = gold, style = filled];\n";
2410      print "site -> \"building$b\" [len = 2, color = firebrick];\n";
[2]2411
2412      my $l = 0;
2413      for my $loc (keys %{$db_building{$building}}) {
2414         $l++;
[63]2415
2416         print "\"location$b-$l\" [label = \"$building" . q{/} . join(q{\n}, split(m{ / }xms, $loc)) . "\", color = black, fillcolor = orange, style = filled];\n";
[33]2417#         print "\"location$b-$l\" [label = \"$building / $loc\", color = black, fillcolor = orange, style = filled];\n";
[4]2418         print "\"building$b\" -> \"location$b-$l\" [len = 2, color = firebrick]\n";
[2]2419
2420         for my $sw (keys %{$db_building{$building}->{$loc}}) {
2421
[156]2422            print "\"$sw:$db_switch_output_port{$sw}\" [label = \"$db_switch_output_port{$sw}\", color = black, fillcolor = lightblue,  peripheries = 2, style = filled];\n";
[2]2423
[25]2424            my $swname  = $sw;
[63]2425               $swname .= q{\n-\n} . "$db_switch_global{$sw}->{model}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{model};
[25]2426            print "\"$sw\" [label = \"$swname\", color = black, fillcolor = palegreen, shape = rect, style = filled];\n";
[4]2427            print "\"location$b-$l\" -> \"$sw\" [len = 2, color = firebrick, arrowtail = dot]\n";
2428            print "\"$sw\" -> \"$sw:$db_switch_output_port{$sw}\" [len=2, style=bold, arrowhead = normal, arrowtail = invdot]\n";
[2]2429
2430
2431            for my $swport (keys %db_switch_connected_on_port) {
[63]2432               my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]2433               next if not $sw_connect eq $sw;
2434               next if $port_connect eq $db_switch_output_port{$sw};
[156]2435               print "\"$sw:$port_connect\" [label = \"$port_connect\", color = black, fillcolor = plum,  peripheries = 1, style = filled];\n";
[4]2436               print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, arrowhead= normal, arrowtail = inv]\n";
[2]2437              }
2438            }
2439         }
2440      }
2441
[63]2442#   print "Switch output port and parent port connection\n";
[2]2443#   print "---------------------------------------------\n";
2444   for my $sw (sort keys %db_switch_output_port) {
2445      if (exists $db_switch_parent{$sw}) {
2446#         printf "   \"%s:%s\" -> \"%s:%s\"\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{switch}, $db_switch_parent{$sw}->{port};
2447         }
2448      else {
2449         printf "   \"%s:%s\" -> internet\n", $sw, $db_switch_output_port{$sw};
2450         }
2451      }
2452   print "\n";
2453
2454#   print "Switch parent and children port inter-connection\n";
2455#   print "------------------------------------------------\n";
[63]2456   for my $swport (sort keys %db_switch_connected_on_port) {
2457      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]2458      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
2459         if (exists $db_switch_output_port{$sw}) {
2460            printf "   \"%s:%s\" -> \"%s:%s\" [color = navyblue]\n", $sw, $db_switch_output_port{$sw}, $sw_connect, $port_connect;
2461            }
2462         else {
2463            printf "   \"%s\"   -> \"%s%s\"\n", $sw, $sw_connect, $port_connect;
2464            }
2465         }
2466      }
2467
2468print "}\n";
[63]2469   return;
[2]2470   }
2471
2472
2473__END__
2474
2475=head1 NAME
2476
2477klask - ports manager and finder for switch
2478
2479
[63]2480=head1 USAGE
[2]2481
[200]2482 klask updatedb [--verbose] [--verb-description|-d] [--chk-hostname|-h] [--chk-location|-l]
[45]2483 klask exportdb --format [txt|html]
[76]2484 klask removedb computer*
[201]2485 klask cleandb  [--verbose] --day number_of_day --repair-dns
[2]2486
[200]2487 klask updatesw [--verbose]
[5]2488 klask exportsw --format [txt|dot]
2489
[136]2490 klask searchdb --kind [host|mac] computer [mac-address]
[2]2491 klask search   computer
[200]2492 klask search-mac-on-switch [--verbose] [--vlan|-i vlan-id] switch mac_addr
[2]2493
[200]2494 klask ip-free  [--verbose] --day number_of_day --format [txt|html] [vlan_name]
[69]2495
[2]2496 klask enable  switch port
2497 klask disable swith port
2498 klask status  swith port
2499
2500
2501=head1 DESCRIPTION
2502
[6]2503klask is a small tool to find where is a host in a big network. klask mean search in brittany.
[200]2504No hight level protocol like CDL, LLDP are use.
2505Everything is just done with SNMP request on MAC address.
[2]2506
[200]2507Limitation : loop cannot be detected and could be problematic when create the map.
2508If you use PVST or MSTP and create loop between VLAN,
2509you have to use portignore functionality on switch port to cut manually loop
2510(see config file below).
2511
2512When you use a management port to administrate the switch,
2513it's not possible to create the map because the switch doesn't have a MAC address,
2514so other switch cannot find downlink port...
2515A way is if you have a computer directly connected on the switch is to put this IP as the fake ip for the switch.
2516The MAC address associated will be use just for the map detection.
2517The fake-ip parameter is defined in the config file.
2518
[6]2519Klask has now a web site dedicated for it !
[2]2520
[111]2521 http://servforge.legi.grenoble-inp.fr/projects/klask
[6]2522
2523
[2]2524=head1 COMMANDS
2525
2526
2527=head2 search
2528
[200]2529This command takes one or more computer in argument.
2530It search a computer on the network and give the port and the switch on which the computer is connected.
[2]2531
2532
2533=head2 enable
2534
[200]2535This command activate a port on a switch by SNMP.
2536So you need to give the switch and the port number on the command line.
[2]2537
2538
2539=head2 disable
2540
[200]2541This command deactivate a port on a switch by SNMP.
2542So you need to give the switch and the port number on the command line.
[2]2543
2544
2545=head2 status
2546
[200]2547This command return the status of a port number on a switch by SNMP.
2548So you need to give the switch name and the port number on the command line.
[2]2549
2550
2551=head2 updatedb
2552
[200]2553This command will scan networks and update a database.
2554To know which are the cmputer scan, you have to configure the file F</etc/klask/klask.conf>.
2555This file is easy to read and write because klask use YAML format and not XML.
[2]2556
2557
[201]2558=head2 exportdb --format [txt|html]
[2]2559
[200]2560This command print the content of the database. There is actually only one format.
2561It's very easy to have more format, it's just need times...
[2]2562
[190]2563=head2 removedb
[2]2564
[190]2565This command remove an entry in the database.
2566There is only one kind of parameter, the IP of the computers to be removed.
2567You can put as many IP as you want...
2568
2569DNS computer name could also a valid entry because a DNS resolver is done at the beginning.
2570
2571=head2 cleandb
2572
2573Remove double entry (same MAC-Address) when the older one is older than X day (--day) the new one.
2574Computer name beginning by 'float' are not really taken into account but could be remove.
2575
2576When reverse DNS has not be done, option --repair-dns force reverse DNS check on unkown host.
2577
[5]2578=head2 updatesw
2579
[200]2580This command build a map of your manageable switch on your network.
2581The list of the switch must be given in the file F</etc/klask/klask.conf>.
[5]2582
2583
2584=head2 exportsw --format [txt|dot]
2585
[200]2586This command print the content of the switch database. There is actually two format.
2587One is just txt for terminal and the other is the dot format from the graphviz environnement.
[5]2588
2589 klask exportsw --format dot > /tmp/map.dot
2590 dot -Tpng /tmp/map.dot > /tmp/map.png
2591
2592
2593
[2]2594=head1 CONFIGURATION
2595
[201]2596Because klask need many parameters, it's not possible actually to use command line parameters for everything.
2597The configuration is done in a F</etc/klask/klask.conf> YAML file.
[200]2598This format have many advantage over XML, it's easier to read and to write !
[2]2599
2600Here an example, be aware with indent, it's important in YAML, do not use tabulation !
2601
2602 default:
2603   community: public
2604   snmpport: 161
2605
2606 network:
[5]2607   labnet:
2608     ip-subnet:
2609       - add: 192.168.1.0/24
2610       - add: 192.168.2.0/24
2611     interface: eth0
[201]2612     vlan-id: 12
[5]2613     main-router: gw1.labnet.local
[2]2614
[5]2615   schoolnet:
2616     ip-subnet:
2617       - add: 192.168.6.0/24
2618       - add: 192.168.7.0/24
2619     interface: eth0.38
[201]2620     vlan-id: 13
[5]2621     main-router: gw2.schoolnet.local
2622
[2]2623 switch:
2624   - hostname: sw1.klask.local
2625     portignore:
2626       - 1
2627       - 2
2628
2629   - hostname: sw2.klask.local
[5]2630     location: BatK / 2 / K203
2631     type: HP2424
[2]2632     portignore:
2633       - 1
2634       - 2
[200]2635     fake-ip: 192.168.9.14
[2]2636
[200]2637I think it's pretty easy to understand.
2638The default section can be overide in any section, if parameter mean something in theses sections.
2639Network to be scan are define in the network section. You must put a add by network.
2640Maybe i will make a delete line to suppress specific computers.
2641The switch section define your switch.
2642You have to write the port number to ignore, this is important if your switchs are cascade.
2643Juste put the ports numbers between switch.
[2]2644
2645
2646=head1 FILES
2647
[92]2648 /etc/klask/klask.conf
[100]2649 /var/lib/klask/klaskdb
2650 /var/lib/klask/switchdb
[2]2651
2652=head1 SEE ALSO
2653
2654Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
2655
2656
2657=head1 VERSION
2658
[36]2659$Id: klask 202 2017-01-29 15:27:43Z g7moreau $
[2]2660
2661
2662=head1 AUTHOR
2663
[5]2664Written by Gabriel Moreau, Grenoble - France
[2]2665
2666
[63]2667=head1 LICENSE AND COPYRIGHT
[2]2668
2669GPL version 2 or later and Perl equivalent
[45]2670
[195]2671Copyright (C) 2005-2017 Gabriel Moreau.
Note: See TracBrowser for help on using the repository browser.