source: trunk/klask @ 76

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