source: trunk/klask @ 135

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