source: trunk/klask @ 70

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