source: trunk/klask @ 211

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