source: trunk/klask @ 74

Last change on this file since 74 was 74, checked in by g7moreau, 13 years ago
  • Add cleandb command to delete duplicate entry when time date are different
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Id Rev
File size: 61.4 KB
Line 
1#!/usr/bin/perl -w
2#
3# Copyright (C) 2005-2008 Gabriel Moreau.
4#
5# $Id: klask 74 2010-11-08 09:50:46Z 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
591 klask updatesw
592 klask exportsw --format [txt|dot]
593
594 klask searchdb computer
595 klask search   computer
596 klask search-mac-on-switch switch mac_addr
597
598 klask ip-free --day number_of_day [vlan_name]
599
600 klask enable  switch port
601 klask disable switch port
602 klask status  switch port
603END
604   return;
605   }
606
607sub cmd_version {
608
609print <<'END';
610Klask - ports manager and finder for switch
611Copyright (C) 2005-2008 Gabriel Moreau
612
613END
614   print ' $Rev: 74 $'."\n";
615   print ' $Date: 2010-11-08 09:50:46 +0000 (Mon, 08 Nov 2010) $'."\n";
616   print ' $Id: klask 74 2010-11-08 09:50:46Z g7moreau $'."\n";
617   return;
618   }
619
620sub cmd_search {
621   my @computer = @_;
622
623   init_switch_names();    #nomme les switchs
624   fastping(@computer);
625   for my $clientname (@computer) {
626      my %resol_arp = resolve_ip_arp_host($clientname);          #resolution arp
627      my %where     = find_switch_port($resol_arp{mac_address}); #retrouve l'emplacement
628      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"
629         unless $where{switch_description} eq 'unknow' and $resol_arp{hostname_fq} eq 'unknow' and $resol_arp{mac_address} eq 'unknow';
630      }
631   return;
632   }
633
634sub cmd_searchdb {
635   my @computer = @_;
636
637   fastping(@computer);
638   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
639
640   LOOP_ON_COMPUTER:
641   for my $clientname (@computer) {
642      my %resol_arp = resolve_ip_arp_host($clientname);      #resolution arp
643      my $ip = $resol_arp{ipv4_address};
644
645      next LOOP_ON_COMPUTER unless exists $computerdb->{$ip};
646
647      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
648      $year += 1900;
649      $mon++;
650      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
651
652      printf "%-22s %2s %-30s %-15s %-18s %s\n",
653         $computerdb->{$ip}{switch_name},
654         $computerdb->{$ip}{switch_port},
655         $computerdb->{$ip}{hostname_fq},
656         $ip,
657         $computerdb->{$ip}{mac_address},
658         $date;
659      }
660   return;
661   }
662
663sub cmd_updatedb {
664   my @network = @_;
665      @network = get_list_network() if not @network;
666
667   test_switchdb_environnement();
668
669   my $computerdb = {};
670      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
671   my $timestamp = time;
672
673   my %computer_not_detected = ();
674   my $timestamp_last_week = $timestamp - (3600 * 24 * 7);
675
676   my $number_of_computer = get_list_ip(@network); # + 1;
677   my $size_of_database   = keys %{$computerdb};
678      $size_of_database   = 1 if $size_of_database == 0;
679   my $i = 0;
680   my $detected_computer = 0;
681
682   init_switch_names('yes');    #nomme les switchs
683
684   { # Remplis le champs portignore des ports d'inter-connection pour chaque switch
685   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
686   my %db_switch_output_port       = %{$switch_connection->{output_port}};
687   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
688   my %db_switch_chained_port = ();
689   for my $swport (keys %db_switch_connected_on_port) {
690      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
691      $db_switch_chained_port{$sw_connect} .= "$port_connect:";
692      }
693   for my $sw (@SWITCH){
694      push @{$sw->{portignore}}, $db_switch_output_port{$sw->{hostname}}  if exists $db_switch_output_port{$sw->{hostname}};
695      if ( exists $db_switch_chained_port{$sw->{hostname}} ) {
696         chop $db_switch_chained_port{$sw->{hostname}};
697         push @{$sw->{portignore}}, split m/ : /xms, $db_switch_chained_port{$sw->{hostname}};
698         }
699#      print "$sw->{hostname} ++ @{$sw->{portignore}}\n";
700      }
701   }
702
703   my %router_mac_ip = ();
704   DETECT_ALL_ROUTER:
705#   for my $one_router ('194.254.66.254') {
706   for my $one_router ( get_list_main_router(@network) ) {
707      my %resol_arp = resolve_ip_arp_host($one_router);
708      $router_mac_ip{ $resol_arp{mac_address} } = $resol_arp{ipv4_address};
709      }
710
711   ALL_NETWORK:
712   for my $net (@network) {
713
714      my @computer = get_list_ip($net);
715      my $current_interface = get_current_interface($net);
716
717      fastping(@computer);
718
719      LOOP_ON_COMPUTER:
720      for my $one_computer (@computer) {
721         $i++;
722
723         my $total_percent = int (($i*100)/$number_of_computer);
724
725         my $localtime = time - $timestamp;
726         my ($sec,$min) = localtime $localtime;
727
728         my $time_elapse = 0;
729            $time_elapse = $localtime * ( 100 - $total_percent) / $total_percent if $total_percent != 0;
730         my ($sec_elapse,$min_elapse) = localtime $time_elapse;
731
732         printf "\rComputer scanned: %4i/%i (%2i%%)",  $i,                 $number_of_computer, $total_percent;
733#         printf ", Computer detected: %4i/%i (%2i%%)", $detected_computer, $size_of_database,   int(($detected_computer*100)/$size_of_database);
734         printf ', detected: %4i/%i (%2i%%)', $detected_computer, $size_of_database,   int(($detected_computer*100)/$size_of_database);
735         printf ' [Time: %02i:%02i / %02i:%02i]', int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60;
736#         printf '  [%02i:%02i/%02i:%02i]', int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60;
737         printf ' %-14s', $one_computer;
738
739         my %resol_arp = resolve_ip_arp_host($one_computer,$current_interface);
740
741         # do not search on router connection (why ?)
742         if ( exists $router_mac_ip{$resol_arp{mac_address}}) {
743            $computer_not_detected{$one_computer} = $current_interface;
744            next LOOP_ON_COMPUTER;
745            }
746
747         # do not search on switch inter-connection
748         if (exists $switch_level{$resol_arp{hostname_fq}}) {
749            $computer_not_detected{$one_computer} = $current_interface;
750            next LOOP_ON_COMPUTER;
751            }
752
753         my $switch_proposal = q{};
754         if (exists $computerdb->{$resol_arp{ipv4_address}} and exists $computerdb->{$resol_arp{ipv4_address}}{switch_hostname}) {
755            $switch_proposal = $computerdb->{$resol_arp{ipv4_address}}{switch_hostname};
756            }
757
758         # do not have a mac address
759         if ($resol_arp{mac_address} eq 'unknow' or (exists $resol_arp{timestamps} and $resol_arp{timestamps} < ($timestamp - 3 * 3600))) {
760            $computer_not_detected{$one_computer} = $current_interface;
761            next LOOP_ON_COMPUTER;
762            }
763
764         my %where = find_switch_port($resol_arp{mac_address},$switch_proposal);
765
766         #192.168.24.156:
767         #  arp: 00:0B:DB:D5:F6:65
768         #  hostname: pcroyon.hmg.priv
769         #  port: 5
770         #  switch: sw-batH-legi:hp2524
771         #  timestamp: 1164355525
772
773         # do not have a mac address
774#         if ($resol_arp{mac_address} eq 'unknow') {
775#            $computer_not_detected{$one_computer} = $current_interface;
776#            next LOOP_ON_COMPUTER;
777#            }
778
779         # detected on a switch
780         if ($where{switch_description} ne 'unknow') {
781            $detected_computer++;
782            $computerdb->{$resol_arp{ipv4_address}} = {
783               hostname_fq        => $resol_arp{hostname_fq},
784               mac_address        => $resol_arp{mac_address},
785               switch_hostname    => $where{switch_hostname},
786               switch_description => $where{switch_description},
787               switch_port        => $where{switch_port},
788               timestamp          => $timestamp,
789               network            => $net,
790               };
791            next LOOP_ON_COMPUTER;
792            }
793
794         # new in the database but where it is ?
795         if (not exists $computerdb->{$resol_arp{ipv4_address}}) {
796            $detected_computer++;
797            $computerdb->{$resol_arp{ipv4_address}} = {
798               hostname_fq        => $resol_arp{hostname_fq},
799               mac_address        => $resol_arp{mac_address},
800               switch_hostname    => $where{switch_hostname},
801               switch_description => $where{switch_description},
802               switch_port        => $where{switch_port},
803               timestamp          => $resol_arp{timestamp},
804               network            => $net,
805               };
806            }
807
808         # mise a jour du nom de la machine si modification dans le dns
809         $computerdb->{$resol_arp{ipv4_address}}{hostname_fq} = $resol_arp{hostname_fq};
810
811         # mise à jour de la date de détection si détection plus récente par arpwatch
812         $computerdb->{$resol_arp{ipv4_address}}{timestamp}   = $resol_arp{timestamp} if exists $resol_arp{timestamp} and $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $resol_arp{timestamp};
813
814         # relance un arping sur la machine si celle-ci n'a pas été détectée depuis plus d'une semaine
815#         push @computer_not_detected, $resol_arp{ipv4_address} if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
816         $computer_not_detected{$resol_arp{ipv4_address}} = $current_interface if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
817
818         }
819      }
820
821   # final end of line at the end of the loop
822   printf "\n";
823
824   my $dirdb = $KLASK_DB_FILE;
825      $dirdb =~ s{ / [^/]* $}{}xms;
826   mkdir "$dirdb", 0755 unless -d "$dirdb";
827   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
828
829   for my $one_computer (keys %computer_not_detected) {
830      my $interface = $computer_not_detected{$one_computer};
831      system "arping -c 1 -w 1 -rR -i $interface $one_computer &>/dev/null";
832#      print  "arping -c 1 -w 1 -rR -i $interface $one_computer 2>/dev/null\n";
833      }
834   return;
835   }
836
837sub cmd_removedb {
838   my @computer = @_;
839
840   test_maindb_environnement();
841
842   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
843
844   LOOP_ON_COMPUTER:
845   for my $one_computer (@computer) {
846
847      if ( $one_computer =~ m/^ $RE_IPv4_ADDRESS $/xms
848            and exists $computerdb->{$one_computer} ) {
849         delete $computerdb->{$one_computer};
850         next;
851         }
852
853      my %resol_arp = resolve_ip_arp_host($one_computer);
854
855      delete $computerdb->{$resol_arp{ipv4_address}} if exists $computerdb->{$resol_arp{ipv4_address}};
856      }
857
858   my $dirdb = $KLASK_DB_FILE;
859      $dirdb =~ s{ / [^/]* $}{}xms;
860   mkdir "$dirdb", 0755 unless -d "$dirdb";
861   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
862   return;
863   }
864
865sub cmd_cleandb {
866   my @options = @_;
867
868   my $days_to_clean = 15;
869   my $verbose;
870   my $database_has_changed;
871
872   my $ret = GetOptionsFromArray(\@options,
873      'day|d=i'   => \$days_to_clean,
874      'verbose|v' => \$verbose,
875      );
876
877   my @vlan_name = get_list_network();
878
879   my $computerdb = {};
880      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
881   my $timestamp = time;
882
883   my $timestamp_barrier = 3600 * 24 * $days_to_clean;
884
885   ALL_VLAN:
886   for my $vlan (@vlan_name) {
887
888      my @ip_list   = get_list_ip($vlan);
889      my %mactimedb = ();
890     
891      LOOP_ON_IP_ADDRESS:
892      for my $ip (@ip_list) {
893
894         next LOOP_ON_IP_ADDRESS if
895            not exists $computerdb->{$ip};
896           
897            #&& $computerdb->{$ip}{timestamp} > $timestamp_barrier;
898         my $ip_timestamp = $computerdb->{$ip}{timestamp};
899         my $ip_mac       = $computerdb->{$ip}{mac_address};
900         
901         $mactimedb{$ip_mac} ||= [ $ip, $ip_timestamp ];
902         
903         if ( $mactimedb{$ip_mac}->[1] - $ip_timestamp > $timestamp_barrier ) {
904            print "remove ip $ip\n" if $verbose;
905            delete $computerdb->{$ip};
906            $database_has_changed++;
907            }
908
909         if ( $ip_timestamp - $mactimedb{$ip_mac}->[1] > $timestamp_barrier ) {
910            print "remove ip ".$mactimedb{$ip_mac}->[0]."\n" if $verbose;
911            delete $computerdb->{$mactimedb{$ip_mac}->[0]};
912            $database_has_changed++;
913            }
914
915         if ( $ip_timestamp > $mactimedb{$ip_mac}->[1]) {
916            $mactimedb{$ip_mac} = [ $ip, $ip_timestamp ];
917            }
918         }
919      }
920
921   if ( $database_has_changed ) {
922      my $dirdb = $KLASK_DB_FILE;
923         $dirdb =~ s{ / [^/]* $}{}xms;
924      mkdir "$dirdb", 0755 unless -d "$dirdb";
925      YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
926      }
927   return;
928   }
929
930sub cmd_exportdb {
931   my @ARGV   = @_;
932
933   my $format = 'txt';
934
935   my $ret = GetOptions(
936      'format|f=s'  => \$format,
937      );
938
939   my %possible_format = (
940      txt  => \&cmd_exportdb_txt,
941      html => \&cmd_exportdb_html,
942      );
943
944   $format = 'txt' if not defined $possible_format{$format};
945
946   $possible_format{$format}->(@ARGV);
947   return;
948   }
949
950sub cmd_exportdb_txt {
951   test_maindb_environnement();
952
953   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
954
955   printf "%-25s %-4s            %-40s %-15s %-18s %-16s %s\n", qw(Switch Port Hostname-FQ IPv4-Address MAC-Address Date VLAN);
956   print "-------------------------------------------------------------------------------------------------------------------------------------------\n";
957
958   LOOP_ON_IP_ADDRESS:
959   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
960
961#      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq 'unknow';
962
963      # to be improve in the future
964      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
965
966# dans le futur
967#      next if $computerdb->{$ip}{hostname_fq} eq 'unknow';
968
969      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
970      $year += 1900;
971      $mon++;
972      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
973
974      printf "%-26s  %2s  <-------  %-40s %-15s %-18s %-16s %s\n",
975         $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
976         $computerdb->{$ip}{switch_port},
977         $computerdb->{$ip}{hostname_fq},
978         $ip,
979         $computerdb->{$ip}{mac_address},
980         $date,
981         $computerdb->{$ip}{network} || '';
982      }
983   return;
984   }
985
986sub cmd_exportdb_html {
987   test_maindb_environnement();
988
989   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
990
991#<link rel="stylesheet" type="text/css" href="style-klask.css" />
992#<script src="sorttable-klask.js"></script>
993
994   print <<'END_HTML';
995<table class="sortable" summary="Klask Host Database">
996 <caption>Klask Host Database</caption>
997 <thead>
998  <tr>
999   <th scope="col" class="klask-header-left">Switch</th>
1000   <th scope="col" class="sorttable_nosort">Port</th>
1001   <th scope="col" class="sorttable_nosort">Link</th>
1002   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
1003   <th scope="col" class="hklask-ipv4">IPv4-Address</th>
1004   <th scope="col" class="sorttable_alpha">MAC-Address</th>
1005   <th scope="col" class="sorttable_alpha">VLAN</th>
1006   <th scope="col" class="klask-header-right">Date</th>
1007  </tr>
1008 </thead>
1009 <tfoot>
1010  <tr>
1011   <th scope="col" class="klask-footer-left">Switch</th>
1012   <th scope="col" class="fklask-port">Port</th>
1013   <th scope="col" class="fklask-link">Link</th>
1014   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
1015   <th scope="col" class="fklask-ipv4">IPv4-Address</th>
1016   <th scope="col" class="fklask-mac">MAC-Address</th>
1017   <th scope="col" class="fklask-vlan">VLAN</th>
1018   <th scope="col" class="klask-footer-right">Date</th>
1019  </tr>
1020 </tfoot>
1021 <tbody>
1022END_HTML
1023
1024   my %mac_count = ();
1025   LOOP_ON_IP_ADDRESS:
1026   foreach my $ip (keys %{$computerdb}) {
1027
1028      # to be improve in the future
1029      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1030
1031      $mac_count{$computerdb->{$ip}{mac_address}}++;
1032      }
1033
1034   my $typerow = 'even';
1035
1036   LOOP_ON_IP_ADDRESS:
1037   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1038
1039      # to be improve in the future
1040      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1041
1042      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1043      $year += 1900;
1044      $mon++;
1045      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1046
1047#      $odd_or_even++;
1048#      my $typerow = $odd_or_even % 2 ? 'odd' : 'even';
1049      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1050
1051      my $switch_hostname = $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description} || 'unkown';
1052      chomp $switch_hostname;
1053      my $switch_hostname_sort = sprintf '%s %3s' ,$switch_hostname, $computerdb->{$ip}{switch_port};
1054
1055      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1056
1057      my $mac_sort = sprintf '%04i-%s', 9999 - $mac_count{$computerdb->{$ip}{mac_address}}, $computerdb->{$ip}{mac_address};
1058
1059      $computerdb->{$ip}{hostname_fq} = 'unknow' if $computerdb->{$ip}{hostname_fq} =~ m/^ \d+ \. \d+ \. \d+ \. \d+ $/xms;
1060      my ( $host_short ) = split m/ \. /xms, $computerdb->{$ip}{hostname_fq};
1061
1062      my $vlan = $computerdb->{$ip}{network} || '';
1063
1064      print <<"END_HTML";
1065  <tr class="$typerow">
1066   <td sorttable_customkey="$switch_hostname_sort">$switch_hostname</td>
1067   <td class="bklask-port">$computerdb->{$ip}{switch_port}</td>
1068   <td><-------</td>
1069   <td sorttable_customkey="$host_short">$computerdb->{$ip}{hostname_fq}</td>
1070   <td sorttable_customkey="$ip_sort">$ip</td>
1071   <td sorttable_customkey="$mac_sort">$computerdb->{$ip}{mac_address}</td>
1072   <td>$vlan</td>
1073   <td>$date</td>
1074  </tr>
1075END_HTML
1076      }
1077
1078   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1079
1080   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1081   my %db_switch_parent            = %{$switch_connection->{parent}};
1082   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1083   my %db_switch                   = %{$switch_connection->{switch_db}};
1084
1085   for my $sw (sort keys %db_switch_output_port) {
1086
1087      my $switch_hostname_sort = sprintf '%s %3s' ,$sw, $db_switch_output_port{$sw};
1088
1089      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1090
1091      if (exists $db_switch_parent{$sw}) {
1092
1093      my $mac_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{mac_address};
1094      my $ipv4_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{ipv4_address};
1095      my $timestamp = $db_switch{$db_switch_parent{$sw}->{switch}}->{timestamp};
1096
1097      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1098      $year += 1900;
1099      $mon++;
1100      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1101
1102      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
1103
1104      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
1105
1106      my ( $host_short ) = sprintf '%s %3s' , split(m/ \. /xms, $db_switch_parent{$sw}->{switch}, 1), $db_switch_parent{$sw}->{port};
1107
1108      print <<"END_HTML";
1109  <tr class="$typerow">
1110   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1111   <td class="bklask-port">$db_switch_output_port{$sw}</>
1112   <td>+--> $db_switch_parent{$sw}->{port}</td>
1113   <td sorttable_customkey="$host_short">$db_switch_parent{$sw}->{switch}</>
1114   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1115   <td sorttable_customkey="$mac_sort">$mac_address</td>
1116   <td></td>
1117   <td>$date</td>
1118  </tr>
1119END_HTML
1120         }
1121      else {
1122         print <<"END_HTML";
1123  <tr class="$typerow">
1124   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1125   <td class="bklask-port">$db_switch_output_port{$sw}</>
1126   <td>+--></td>
1127   <td sorttable_customkey="router">router</>
1128   <td sorttable_customkey="999999999999"></td>
1129   <td sorttable_customkey="99999"></td>
1130   <td></td>
1131   <td></td>
1132  </tr>
1133END_HTML
1134         }
1135      }
1136
1137   for my $swport (sort keys %db_switch_connected_on_port) {
1138      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1139      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1140
1141         my $switch_hostname_sort = sprintf '%s %3s' ,$sw_connect, $port_connect;
1142
1143      my $mac_address = $db_switch{$sw}->{mac_address};
1144      my $ipv4_address = $db_switch{$sw}->{ipv4_address};
1145      my $timestamp = $db_switch{$sw}->{timestamp};
1146
1147      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1148      $year += 1900;
1149      $mon++;
1150      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year,$mon,$mday,$hour,$min;
1151
1152      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
1153
1154      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
1155
1156      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1157
1158         if (exists $db_switch_output_port{$sw}) {
1159
1160            my ( $host_short ) = sprintf '%s %3s' , split( m/\./xms, $sw, 1), $db_switch_output_port{$sw};
1161
1162            print <<"END_HTML";
1163  <tr class="$typerow">
1164   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1165   <td class="bklask-port">$port_connect</>
1166   <td>&lt;--+ $db_switch_output_port{$sw}</td>
1167   <td sorttable_customkey="$host_short">$sw</>
1168   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1169   <td sorttable_customkey="$mac_sort">$mac_address</td>
1170   <td></td>
1171   <td>$date</td>
1172  </tr>
1173END_HTML
1174            }
1175         else {
1176            print <<"END_HTML";
1177  <tr class="$typerow">
1178   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1179   <td class="bklask-port">$port_connect</>
1180   <td>&lt;--+</td>
1181   <td sorttable_customkey="$sw">$sw</>
1182   <td sorttable_customkey="">$ipv4_address</td>
1183   <td sorttable_customkey="">$mac_address</td>
1184   <td></td>
1185   <td>$date</td>
1186  </tr>
1187END_HTML
1188            }
1189         }
1190      }
1191
1192   print <<'END_HTML';
1193 </tbody>
1194</table>
1195END_HTML
1196   return;
1197   }
1198
1199sub cmd_iplocation {
1200   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
1201
1202   LOOP_ON_IP_ADDRESS:
1203   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1204
1205      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1206
1207      my $sw_hostname = $computerdb->{$ip}{switch_hostname} || q{};
1208      next if $sw_hostname eq 'unknow';
1209
1210      my $sw_location = q{};
1211      for my $sw (@SWITCH) {
1212         next if $sw_hostname ne $sw->{hostname};
1213         $sw_location = $sw->{location};
1214         last;
1215         }
1216
1217      printf "%s: \"%s\"\n", $ip, $sw_location if not $sw_location eq q{};
1218      }
1219   return;
1220   }
1221
1222sub cmd_ip_free {
1223   my @vlan_name = @_;
1224
1225   my $days_to_dead = 365 * 2;
1226   my $format = 'txt';
1227
1228   my $ret = GetOptionsFromArray(\@vlan_name,
1229      'day|d=i'      => \$days_to_dead,
1230      'format|f=s'   => \$format,
1231      );
1232
1233   my %possible_format = (
1234      txt  => \&cmd_ip_free_txt,
1235      html => \&cmd_ip_free_html,
1236      );
1237   $format = 'txt' if not defined $possible_format{$format};
1238
1239   @vlan_name = get_list_network() if not @vlan_name;
1240
1241   my $computerdb = {};
1242      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
1243   my $timestamp = time;
1244
1245   my $timestamp_barrier = $timestamp - (3600 * 24 * $days_to_dead );
1246
1247   my %result_ip = ();
1248
1249   ALL_NETWORK:
1250   for my $vlan (@vlan_name) {
1251
1252      my @ip_list = get_list_ip($vlan);
1253#      my $current_interface = get_current_interface($vlan);
1254     
1255      LOOP_ON_IP_ADDRESS:
1256      for my $ip (@ip_list) {
1257
1258         next LOOP_ON_IP_ADDRESS if
1259            exists $computerdb->{$ip}
1260            && $computerdb->{$ip}{timestamp} > $timestamp_barrier;
1261
1262         my $ip_date_last_detection = '';
1263         if (exists $computerdb->{$ip}) {
1264            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1265            $year += 1900;
1266            $mon++;
1267            $ip_date_last_detection = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1268            }
1269
1270         $result_ip{$ip} ||= {};
1271         $result_ip{$ip}->{date_last_detection} = $ip_date_last_detection;
1272
1273         my $packed_ip = scalar gethostbyname($ip);
1274         my $hostname_fq = 'unknown';
1275            $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) || 'unknown' if defined $packed_ip;
1276         $result_ip{$ip}->{hostname_fq} = $hostname_fq;
1277         
1278         $result_ip{$ip}->{vlan} = $vlan;
1279         }
1280      }
1281
1282   $possible_format{$format}->(%result_ip);
1283   }
1284
1285sub cmd_ip_free_txt {
1286   my %result_ip = @_;
1287   
1288   printf "%-15s %-40s %-16s %s\n", qw(IPv4-Address Hostname-FQ Date VLAN);
1289   print "-------------------------------------------------------------------------------\n";
1290   LOOP_ON_IP_ADDRESS:
1291   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1292         printf "%-15s %-40s %-16s %s\n", $ip, $result_ip{$ip}->{hostname_fq}, $result_ip{$ip}->{date_last_detection}, $result_ip{$ip}->{vlan};
1293      }
1294   }
1295
1296sub cmd_ip_free_html {
1297   my %result_ip = @_;
1298
1299   print <<'END_HTML';
1300<table class="sortable" summary="Klask Free IP Database">
1301 <caption>Klask Free IP Database</caption>
1302 <thead>
1303  <tr>
1304   <th scope="col" class="klask-header-left">IPv4-Address</th>
1305   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
1306   <th scope="col" class="sorttable_alpha">VLAN</th>
1307   <th scope="col" class="klask-header-right">Date</th>
1308  </tr>
1309 </thead>
1310 <tfoot>
1311  <tr>
1312   <th scope="col" class="klask-footer-left">IPv4-Address</th>
1313   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
1314   <th scope="col" class="fklask-vlan">VLAN</th>
1315   <th scope="col" class="klask-footer-right">Date</th>
1316  </tr>
1317 </tfoot>
1318 <tbody>
1319END_HTML
1320
1321   my $typerow = 'even';
1322
1323   LOOP_ON_IP_ADDRESS:
1324   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1325
1326      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1327
1328      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1329      my ( $host_short ) = split m/ \. /xms, $result_ip{$ip}->{hostname_fq};
1330
1331      print <<"END_HTML";
1332  <tr class="$typerow">
1333   <td sorttable_customkey="$ip_sort">$ip</td>
1334   <td sorttable_customkey="$host_short">$result_ip{$ip}->{hostname_fq}</td>
1335   <td>$result_ip{$ip}->{vlan}</td>
1336   <td>$result_ip{$ip}->{date_last_detection}</td>
1337  </tr>
1338END_HTML
1339      }
1340   print <<'END_HTML';
1341 </tbody>
1342</table>
1343END_HTML
1344   }
1345
1346sub cmd_enable {
1347   my $switch = shift;
1348   my $port   = shift;
1349
1350   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
1351   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
1352   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
1353   return;
1354   }
1355
1356sub cmd_disable {
1357   my $switch = shift;
1358   my $port   = shift;
1359
1360   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
1361   return;
1362   }
1363
1364sub cmd_status {
1365   my $switch = shift;
1366   my $port   = shift;
1367
1368   system "snmpget -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port";
1369   return;
1370   }
1371
1372sub cmd_search_mac_on_switch {
1373   my $switch_name = shift || q{};
1374   my $mac_address = shift || q{};
1375
1376   if ($switch_name eq q{} or $mac_address eq q{}) {
1377      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
1378      }
1379
1380   if (not defined $SWITCH_DB{$switch_name}) {
1381      die "Switch $switch_name must be defined in klask configuration file\n";
1382      }
1383
1384   my $sw = $SWITCH_DB{$switch_name};
1385   my %session = ( -hostname => $sw->{hostname} );
1386      $session{-version} = $sw->{version}   || 1;
1387      $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1388   if (exists $sw->{version} and $sw->{version} eq '3') {
1389      $session{-username} = $sw->{username} || 'snmpadmin';
1390      }
1391   else {
1392      $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1393      }
1394
1395   my $research = '1.3.6.1.2.1.17.4.3.1.2' . arp_hex_to_dec($mac_address);
1396   print "Klask search OID $research on switch $switch_name\n";
1397
1398   my ($session, $error) = Net::SNMP->session( %session );
1399   print "$error \n" if $error;
1400
1401   my $result = $session->get_request(
1402      -varbindlist => [$research]
1403      );
1404
1405   if (not defined $result or $result->{$research} eq 'noSuchInstance') {
1406      print "Klask do not find MAC $mac_address on switch $switch_name\n";
1407      $session->close;
1408      }
1409
1410   my $swport = $result->{$research};
1411   $session->close;
1412
1413   print "Klask find MAC $mac_address on switch $switch_name port $swport\n";
1414   return;
1415   }
1416
1417sub cmd_updatesw {
1418
1419   init_switch_names('yes');    #nomme les switchs
1420   print "\n";
1421
1422   my %where = ();
1423   my %db_switch_output_port = ();
1424   my %db_switch_ip_hostname = ();
1425
1426   DETECT_ALL_ROUTER:
1427#   for my $one_computer ('194.254.66.254') {
1428   for my $one_router ( get_list_main_router(get_list_network()) ) {
1429      my %resol_arp = resolve_ip_arp_host($one_router, q{*}, q{low}); # resolution arp
1430      next DETECT_ALL_ROUTER if $resol_arp{mac_address} eq 'unknow';
1431      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # retrouve les emplacements des routeurs
1432      }
1433
1434   ALL_ROUTER_IP_ADDRESS:
1435   for my $ip (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
1436
1437      next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip}; # /a priori/ idiot car ne sers à rien...
1438
1439      ALL_SWITCH_CONNECTED:
1440      for my $switch_detected ( keys %{$where{$ip}} ) {
1441
1442         my $switch = $where{$ip}->{$switch_detected};
1443
1444         next ALL_SWITCH_CONNECTED if $switch->{port} eq '0';
1445
1446         $db_switch_output_port{$switch->{hostname}} = $switch->{port};
1447         }
1448      }
1449
1450   my %db_switch_link_with = ();
1451
1452   my @list_switch_ip = ();
1453   my @list_switch_ipv4 = ();
1454   for my $sw (@SWITCH){
1455      push @list_switch_ip, $sw->{hostname};
1456      }
1457
1458   my $timestamp = time;
1459
1460   ALL_SWITCH:
1461   for my $one_computer (@list_switch_ip) {
1462      my %resol_arp = resolve_ip_arp_host($one_computer, q{*}, q{low}); # arp resolution
1463      next ALL_SWITCH if $resol_arp{mac_address} eq 'unknow';
1464
1465      push @list_switch_ipv4,$resol_arp{ipv4_address};
1466
1467      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # find port on all switch
1468#my @l = ();
1469#for my $c  ( keys %{$where{$resol_arp{ipv4_address}}} ) {
1470#push @l, "$c -+ "; #$where{$resol_arp{ipv4_address}}->{$c}{hostname};
1471#}
1472#print "DEBUG  $one_computer $resol_arp{ipv4_address} $resol_arp{mac_address} --- @l\n";
1473
1474      $db_switch_ip_hostname{$resol_arp{ipv4_address}} = $resol_arp{hostname_fq};
1475
1476      $SWITCH_DB{$one_computer}->{ipv4_address} = $resol_arp{ipv4_address};
1477      $SWITCH_DB{$one_computer}->{mac_address}  = $resol_arp{mac_address};
1478      $SWITCH_DB{$one_computer}->{timestamp}    = $timestamp;
1479      }
1480
1481   ALL_SWITCH_IP_ADDRESS:
1482   for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
1483
1484      next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
1485
1486      DETECTED_SWITCH:
1487      for my $switch_detected ( keys %{$where{$ip}} ) {
1488
1489         next DETECTED_SWITCH if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostname{$ip}};
1490
1491         my $switch = $where{$ip}->{$switch_detected};
1492#print "DEBUG1 :  $db_switch_ip_hostname{$ip} / $switch->{hostname} : $switch->{port}\n" if  $switch->{hostname} =~ m/sw3-batA0-3s/;
1493
1494         next if $switch->{port}     eq '0';
1495         next if $switch->{port}     eq $db_switch_output_port{$switch->{hostname}};
1496         next if $switch->{hostname} eq $db_switch_ip_hostname{$ip}; # $computerdb->{$ip}{hostname};
1497
1498         $db_switch_link_with{ $db_switch_ip_hostname{$ip} } ||= {};
1499         $db_switch_link_with{ $db_switch_ip_hostname{$ip} }->{ $switch->{hostname} } = $switch->{port};
1500#print "DEBUG :  $db_switch_ip_hostname{$ip} / $switch->{hostname} : $switch->{port}\n" if $switch->{hostname} !~ m/sw3-batA0-3s/;
1501         }
1502
1503      }
1504
1505   my %db_switch_connected_on_port = ();
1506   my $maybe_more_than_one_switch_connected = 'yes';
1507
1508   while ($maybe_more_than_one_switch_connected eq 'yes') {
1509      for my $sw (keys %db_switch_link_with) {
1510         for my $connect (keys %{$db_switch_link_with{$sw}}) {
1511
1512            my $port = $db_switch_link_with{$sw}->{$connect};
1513
1514            $db_switch_connected_on_port{"$connect:$port"} ||= {};
1515            $db_switch_connected_on_port{"$connect:$port"}->{$sw}++; # Just to define the key
1516            }
1517         }
1518
1519      $maybe_more_than_one_switch_connected  = 'no';
1520
1521      SWITCH_AND_PORT:
1522      for my $swport (keys %db_switch_connected_on_port) {
1523
1524         next if keys %{$db_switch_connected_on_port{$swport}} == 1;
1525
1526         $maybe_more_than_one_switch_connected = 'yes';
1527
1528         my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1529         my @sw_on_same_port = keys %{$db_switch_connected_on_port{$swport}};
1530
1531         CONNECTED:
1532         for my $sw_connected (@sw_on_same_port) {
1533
1534            next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
1535
1536            $db_switch_connected_on_port{$swport} = {$sw_connected => 1};
1537
1538            for my $other_sw (@sw_on_same_port) {
1539               next if $other_sw eq $sw_connected;
1540
1541               delete $db_switch_link_with{$other_sw}->{$sw_connect};
1542               }
1543
1544            # We can not do better for this switch for this loop
1545            next SWITCH_AND_PORT;
1546            }
1547         }
1548      }
1549
1550   my %db_switch_parent =();
1551
1552   for my $sw (keys %db_switch_link_with) {
1553      for my $connect (keys %{$db_switch_link_with{$sw}}) {
1554
1555         my $port = $db_switch_link_with{$sw}->{$connect};
1556
1557         $db_switch_connected_on_port{"$connect:$port"} ||= {};
1558         $db_switch_connected_on_port{"$connect:$port"}->{$sw} = $port;
1559
1560         $db_switch_parent{$sw} = {switch => $connect, port => $port};
1561         }
1562      }
1563
1564   print "Switch output port and parent port connection\n";
1565   print "---------------------------------------------\n";
1566   for my $sw (sort keys %db_switch_output_port) {
1567      if (exists $db_switch_parent{$sw}) {
1568         printf "%-25s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1569         }
1570      else {
1571         printf "%-25s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1572         }
1573      }
1574   print "\n";
1575
1576   print "Switch parent and children port inter-connection\n";
1577   print "------------------------------------------------\n";
1578   for my $swport (sort keys %db_switch_connected_on_port) {
1579      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1580      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1581         if (exists $db_switch_output_port{$sw}) {
1582            printf "%-25s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1583            }
1584         else {
1585            printf "%-25s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1586            }
1587         }
1588      }
1589
1590   my $switch_connection = {
1591      output_port       => \%db_switch_output_port,
1592      parent            => \%db_switch_parent,
1593      connected_on_port => \%db_switch_connected_on_port,
1594      link_with         => \%db_switch_link_with,
1595      switch_db         => \%SWITCH_DB,
1596      };
1597
1598   YAML::Syck::DumpFile("$KLASK_SW_FILE", $switch_connection);
1599   return;
1600   }
1601
1602sub cmd_exportsw {
1603   my @ARGV   = @_;
1604
1605   test_switchdb_environnement();
1606
1607   my $format = 'txt';
1608
1609   my $ret = GetOptions(
1610      'format|f=s'  => \$format,
1611      );
1612
1613   my %possible_format = (
1614      txt => \&cmd_exportsw_txt,
1615      dot => \&cmd_exportsw_dot,
1616      );
1617
1618   $format = 'txt' if not defined $possible_format{$format};
1619
1620   $possible_format{$format}->(@ARGV);
1621   return;
1622   }
1623
1624sub cmd_exportsw_txt {
1625
1626   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1627
1628   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1629   my %db_switch_parent            = %{$switch_connection->{parent}};
1630   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1631
1632   print "Switch output port and parent port connection\n";
1633   print "---------------------------------------------\n";
1634   for my $sw (sort keys %db_switch_output_port) {
1635      if (exists $db_switch_parent{$sw}) {
1636         printf "%-25s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1637         }
1638      else {
1639         printf "%-25s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1640         }
1641      }
1642   print "\n";
1643
1644   print "Switch parent and children port inter-connection\n";
1645   print "------------------------------------------------\n";
1646   for my $swport (sort keys %db_switch_connected_on_port) {
1647      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1648      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1649         if (exists $db_switch_output_port{$sw}) {
1650            printf "%-25s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1651            }
1652         else {
1653            printf "%-25s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1654            }
1655         }
1656      }
1657   return;
1658   }
1659
1660sub cmd_exportsw_dot {
1661
1662   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1663
1664   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1665   my %db_switch_parent            = %{$switch_connection->{parent}};
1666   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1667   my %db_switch_link_with         = %{$switch_connection->{link_with}};
1668   my %db_switch_global            = %{$switch_connection->{switch_db}};
1669
1670   my %db_building= ();
1671   for my $sw (@SWITCH) {
1672      my ($building, $location) = split m/ \/ /xms, $sw->{location}, 2;
1673      $db_building{$building} ||= {};
1674      $db_building{$building}->{$location} ||= {};
1675      $db_building{$building}->{$location}{ $sw->{hostname} } = 'y';
1676      }
1677
1678
1679   print "digraph G {\n";
1680
1681   print "site [label = \"site\", color = black, fillcolor = gold, shape = invhouse, style = filled];\n";
1682   print "internet [label = \"internet\", color = black, fillcolor = cyan, shape = house, style = filled];\n";
1683
1684   my $b=0;
1685   for my $building (keys %db_building) {
1686      $b++;
1687
1688      print "\"building$b\" [label = \"$building\", color = black, fillcolor = gold, style = filled];\n";
1689      print "site -> \"building$b\" [len = 2, color = firebrick];\n";
1690
1691      my $l = 0;
1692      for my $loc (keys %{$db_building{$building}}) {
1693         $l++;
1694
1695         print "\"location$b-$l\" [label = \"$building" . q{/} . join(q{\n}, split(m{ / }xms, $loc)) . "\", color = black, fillcolor = orange, style = filled];\n";
1696#         print "\"location$b-$l\" [label = \"$building / $loc\", color = black, fillcolor = orange, style = filled];\n";
1697         print "\"building$b\" -> \"location$b-$l\" [len = 2, color = firebrick]\n";
1698
1699         for my $sw (keys %{$db_building{$building}->{$loc}}) {
1700
1701            print "\"$sw:$db_switch_output_port{$sw}\" [label = $db_switch_output_port{$sw}, color = black, fillcolor = lightblue,  peripheries = 2, style = filled];\n";
1702
1703            my $swname  = $sw;
1704               $swname .= q{\n-\n} . "$db_switch_global{$sw}->{model}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{model};
1705            print "\"$sw\" [label = \"$swname\", color = black, fillcolor = palegreen, shape = rect, style = filled];\n";
1706            print "\"location$b-$l\" -> \"$sw\" [len = 2, color = firebrick, arrowtail = dot]\n";
1707            print "\"$sw\" -> \"$sw:$db_switch_output_port{$sw}\" [len=2, style=bold, arrowhead = normal, arrowtail = invdot]\n";
1708
1709
1710            for my $swport (keys %db_switch_connected_on_port) {
1711               my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1712               next if not $sw_connect eq $sw;
1713               next if $port_connect eq $db_switch_output_port{$sw};
1714               print "\"$sw:$port_connect\" [label = $port_connect, color = black, fillcolor = plum,  peripheries = 1, style = filled];\n";
1715               print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, arrowhead= normal, arrowtail = inv]\n";
1716              }
1717            }
1718         }
1719      }
1720
1721#   print "Switch output port and parent port connection\n";
1722#   print "---------------------------------------------\n";
1723   for my $sw (sort keys %db_switch_output_port) {
1724      if (exists $db_switch_parent{$sw}) {
1725#         printf "   \"%s:%s\" -> \"%s:%s\"\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{switch}, $db_switch_parent{$sw}->{port};
1726         }
1727      else {
1728         printf "   \"%s:%s\" -> internet\n", $sw, $db_switch_output_port{$sw};
1729         }
1730      }
1731   print "\n";
1732
1733#   print "Switch parent and children port inter-connection\n";
1734#   print "------------------------------------------------\n";
1735   for my $swport (sort keys %db_switch_connected_on_port) {
1736      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1737      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1738         if (exists $db_switch_output_port{$sw}) {
1739            printf "   \"%s:%s\" -> \"%s:%s\" [color = navyblue]\n", $sw, $db_switch_output_port{$sw}, $sw_connect, $port_connect;
1740            }
1741         else {
1742            printf "   \"%s\"   -> \"%s%s\"\n", $sw, $sw_connect, $port_connect;
1743            }
1744         }
1745      }
1746
1747print "}\n";
1748   return;
1749   }
1750
1751
1752__END__
1753
1754=head1 NAME
1755
1756klask - ports manager and finder for switch
1757
1758
1759=head1 USAGE
1760
1761 klask updatedb
1762 klask exportdb --format [txt|html]
1763
1764 klask updatesw
1765 klask exportsw --format [txt|dot]
1766
1767 klask searchdb computer
1768 klask search   computer
1769 klask search-mac-on-switch switch mac_addr
1770
1771 klask ip-free --day number_of_day [vlan_name]
1772
1773 klask enable  switch port
1774 klask disable swith port
1775 klask status  swith port
1776
1777
1778=head1 DESCRIPTION
1779
1780klask is a small tool to find where is a host in a big network. klask mean search in brittany.
1781
1782Klask has now a web site dedicated for it !
1783
1784 http://servforge.legi.inpg.fr/projects/klask
1785
1786
1787=head1 COMMANDS
1788
1789
1790=head2 search
1791
1792This 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.
1793
1794
1795=head2 enable
1796
1797This command activate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1798
1799
1800=head2 disable
1801
1802This command deactivate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1803
1804
1805=head2 status
1806
1807This 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.
1808
1809
1810=head2 updatedb
1811
1812This 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.
1813
1814
1815=head2 exportdb
1816
1817This 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...
1818
1819
1820=head2 updatesw
1821
1822This 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.
1823
1824
1825=head2 exportsw --format [txt|dot]
1826
1827This 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.
1828
1829 klask exportsw --format dot > /tmp/map.dot
1830 dot -Tpng /tmp/map.dot > /tmp/map.png
1831
1832
1833
1834=head1 CONFIGURATION
1835
1836Because 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 !
1837
1838Here an example, be aware with indent, it's important in YAML, do not use tabulation !
1839
1840 default:
1841   community: public
1842   snmpport: 161
1843
1844 network:
1845   labnet:
1846     ip-subnet:
1847       - add: 192.168.1.0/24
1848       - add: 192.168.2.0/24
1849     interface: eth0
1850     main-router: gw1.labnet.local
1851
1852   schoolnet:
1853     ip-subnet:
1854       - add: 192.168.6.0/24
1855       - add: 192.168.7.0/24
1856     interface: eth0.38
1857     main-router: gw2.schoolnet.local
1858
1859 switch:
1860   - hostname: sw1.klask.local
1861     portignore:
1862       - 1
1863       - 2
1864
1865   - hostname: sw2.klask.local
1866     location: BatK / 2 / K203
1867     type: HP2424
1868     portignore:
1869       - 1
1870       - 2
1871
1872I 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.
1873
1874
1875=head1 FILES
1876
1877 /etc/klask.conf
1878 /var/cache/klask/klaskdb
1879 /var/cache/klask/switchdb
1880
1881=head1 SEE ALSO
1882
1883Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
1884
1885
1886=head1 VERSION
1887
1888$Id: klask 74 2010-11-08 09:50:46Z g7moreau $
1889
1890
1891=head1 AUTHOR
1892
1893Written by Gabriel Moreau, Grenoble - France
1894
1895
1896=head1 LICENSE AND COPYRIGHT
1897
1898GPL version 2 or later and Perl equivalent
1899
1900Copyright (C) 2005-2009 Gabriel Moreau.
Note: See TracBrowser for help on using the repository browser.