source: trunk/klask @ 70

Last change on this file since 70 was 70, checked in by g7moreau, 14 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
RevLine 
[2]1#!/usr/bin/perl -w
[32]2#
3# Copyright (C) 2005-2008 Gabriel Moreau.
4#
5# $Id: klask 70 2010-11-02 15:55:37Z g7moreau $
[2]6
7use strict;
[3]8use warnings;
[63]9use version; our $VERSION = qv('0.5.5');
[2]10
[63]11use Readonly;
[64]12use FileHandle;
[2]13use Net::SNMP;
[44]14#use YAML;
15use YAML::Syck;
[2]16use Net::Netmask;
17use Net::CIDR::Lite;
18use NetAddr::IP;
[69]19use Getopt::Long qw(GetOptions GetOptionsFromArray);;
[68]20use Socket;
[2]21
[8]22# apt-get install snmp fping libnet-cidr-lite-perl libnet-netmask-perl libnet-snmp-perl libnetaddr-ip-perl libyaml-perl
[68]23# libcrypt-des-perl libcrypt-hcesha-perl libdigest-hmac-perl
[23]24# arping fping bind9-host arpwatch
[2]25
[26]26my $KLASK_VAR      = '/var/cache/klask';
[13]27my $KLASK_CFG_FILE = '/etc/klask.conf';
[28]28my $KLASK_DB_FILE  = "$KLASK_VAR/klaskdb";
29my $KLASK_SW_FILE  = "$KLASK_VAR/switchdb";
[2]30
[26]31test_running_environnement();
32
[44]33my $KLASK_CFG = YAML::Syck::LoadFile("$KLASK_CFG_FILE");
[2]34
[63]35my %DEFAULT = %{ $KLASK_CFG->{default} };
36my @SWITCH  = @{ $KLASK_CFG->{switch}  };
[2]37
[11]38my %switch_level = ();
[63]39my %SWITCH_DB    = ();
[2]40LEVEL_OF_EACH_SWITCH:
[11]41for my $sw (@SWITCH){
42   $switch_level{$sw->{hostname}} = $sw->{level} || $DEFAULT{switch_level}  || 2;
[25]43   $SWITCH_DB{$sw->{hostname}} = $sw;
[2]44   }
[63]45@SWITCH = reverse sort { $switch_level{$a->{hostname}} <=> $switch_level{$b->{hostname}} } @{$KLASK_CFG->{switch}};
[2]46
[15]47my %SWITCH_PORT_COUNT = ();
[2]48
[11]49my %CMD_DB = (
[2]50   help       => \&cmd_help,
[36]51   version    => \&cmd_version,
[2]52   exportdb   => \&cmd_exportdb,
53   updatedb   => \&cmd_updatedb,
54   searchdb   => \&cmd_searchdb,
[4]55   removedb   => \&cmd_removedb,
[2]56   search     => \&cmd_search,
57   enable     => \&cmd_enable,
58   disable    => \&cmd_disable,
59   status     => \&cmd_status,
[4]60   updatesw   => \&cmd_updatesw,
61   exportsw   => \&cmd_exportsw,
[2]62   iplocation => \&cmd_iplocation,
[69]63   'ip-free'  => \&cmd_ip_free,
[35]64   'search-mac-on-switch' => \&cmd_search_mac_on_switch,
[2]65   );
66
[63]67Readonly my %INTERNAL_PORT_MAP => (
[2]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   );
[63]77Readonly my %INTERNAL_PORT_MAP_REV => reverse %INTERNAL_PORT_MAP;
[2]78
[63]79Readonly my %SWITCH_KIND => (
[20]80   J3299A => { model => 'HP224M',     match => 'HP J3299A ProCurve Switch 224M'  },
81   J4120A => { model => 'HP1600M',    match => 'HP J4120A ProCurve Switch 1600M' },
[27]82   J9029A => { model => 'HP1800-8G',  match => 'PROCURVE J9029A'                 },
[68]83   J9449A => { model => 'HP1810-8G',  match => 'HP ProCurve 1810G - 8 GE'        },
[20]84   J4093A => { model => 'HP2424M',    match => 'HP J4093A ProCurve Switch 2424M' },
85   J4813A => { model => 'HP2524',     match => 'HP J4813A ProCurve Switch 2524'  },
[21]86   J4900A => { model => 'HP2626A',    match => 'HP J4900A ProCurve Switch 2626'  },
[42]87   J4900B => { model => 'HP2626B',    match => 'J4900B.+?Switch 2626'            },# ProCurve J4900B Switch 2626 # HP J4900B ProCurve Switch 2626
[30]88   J4899B => { model => 'HP2650',     match => 'ProCurve J4899B Switch 2650'     },
[20]89   J9021A => { model => 'HP2810-24G', match => 'ProCurve J9021A Switch 2810-24G' },
[41]90   J9022A => { model => 'HP2810-48G', match => 'ProCurve J9022A Switch 2810-48G' },
[20]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'                },
[18]94   );
[63]95
96Readonly my %OID_NUMBER => (
[68]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',
[19]101   );
[18]102
[63]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
[2]107################
108# principal
109################
110
[11]111my $cmd = shift @ARGV || 'help';
112if (defined $CMD_DB{$cmd}) {
113   $CMD_DB{$cmd}->(@ARGV);
[2]114   }
115else {
[63]116   print {*STDERR} "klask: command $cmd not found\n\n";
[11]117   $CMD_DB{help}->();
[2]118   exit 1;
119   }
120
121exit;
122
[26]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";
[63]126   return;
[26]127   }
128
[34]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";
[63]131   return;
[34]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";
[63]136   return;
[34]137   }
138
[2]139###
140# fast ping dont l'objectif est de remplir la table arp de la machine
141sub fastping {
[68]142   # Launch this command without waiting...
143   system "fping -c 1 @_ >/dev/null 2>&1 &";
[63]144   return;
[2]145   }
146
[63]147sub shell_command {
148   my $cmd = shift;
149
[64]150   my $fh     = new FileHandle;
151   my $result = '';
[63]152   open $fh, q{-|}, "$cmd" or die "Can't exec $cmd\n";
[64]153   $result .= <$fh>;
[63]154   close $fh;
155   chomp $result;
156   return $result;
157   }
158
[2]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;
[63]163   my $interface = shift || q{*};
164   my $type      = shift || q{fast};
[2]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`;
[68]173#   if ( not $param_ip_or_host =~ m/^\d+ \. \d+ \. \d+ \. \d+$/xms ) {
174#      $param_ip_or_host =~ s/ \. .* //xms;
175#      }
[2]176
[68]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
[2]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
[44]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";
[68]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";
[62]207   my $cmd_arpwatch = shell_command $cmd;
208   my ($arp, $ip, $timestamp, $host) = split m/ \s+ /xms, $cmd_arpwatch;
[44]209
[68]210#   $ret{ipv4_address} = $ip        if $ip;
[2]211   $ret{mac_address}  = $arp       if $arp;
212   $ret{timestamp}    = $timestamp if $timestamp;
213
[63]214   my $nowtimestamp = time;
[3]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
[63]221   # resultat de la commande arp
[2]222   # tech7meylan.hmg.inpg.fr (194.254.66.240) at 00:14:22:45:28:A9 [ether] on eth0
[44]223   # sw2-batF0-legi.hmg.priv (192.168.22.112) at 00:30:c1:76:9c:01 [ether] on eth0.37
[63]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      }
[2]228
[63]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.
[68]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#      }
[2]235
[63]236   # resultat de la commande host si parametre est hostname
237   # tech7meylan.hmg.inpg.fr has address 194.254.66.240
[68]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#      }
[2]241
[65]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   #    }
[63]247   #$ret{hostname_fq}  = $param_ip_or_host if not defined $1 and $ret{hostname_fq} eq 'unknow';
[2]248
[63]249   if ($ret{mac_address} ne 'unknow') {
[2]250      my @paquets = ();
[63]251      foreach ( split m/ : /xms, $ret{mac_address} ) {
252         my @chars = split m//xms, uc "00$_";
[2]253         push @paquets, "$chars[-2]$chars[-1]";
254         }
[63]255      $ret{mac_address} = join q{:}, @paquets;
[2]256      }
257
258   return %ret;
259   }
260
[20]261# Find Surname of a switch
262sub get_switch_model {
[22]263   my $sw_snmp_description = shift || 'unknow';
[63]264
[20]265   for my $sw_kind (keys %SWITCH_KIND) {
[64]266      next if not $sw_snmp_description =~ m/$SWITCH_KIND{$sw_kind}->{match}/ms; # option xms break search, why ?
[63]267
[20]268      return $SWITCH_KIND{$sw_kind}->{model};
269      }
[63]270
[22]271   return $sw_snmp_description;
[20]272   }
273
[2]274###
275# va rechercher le nom des switchs pour savoir qui est qui
[4]276sub init_switch_names {
[2]277   my $verbose = shift;
[63]278
[14]279   printf "%-25s                %-25s %s\n",'Switch','Description','Type';
280   print "-------------------------------------------------------------------------\n" if $verbose;
[2]281
282   INIT_EACH_SWITCH:
[11]283   for my $sw (@SWITCH) {
[3]284      my %session = ( -hostname   => $sw->{hostname} );
285         $session{-version} = $sw->{version}   || 1;
[11]286         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
[63]287         if (exists $sw->{version} and $sw->{version} eq '3') {
[3]288            $session{-username} = $sw->{username} || 'snmpadmin';
289            }
290         else {
[11]291            $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
[3]292            }
293
294      $sw->{local_session} = \%session;
295
296      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
[14]297      print "$error \n" if $error;
298
[2]299      my $result = $session->get_request(
[18]300         -varbindlist => [
[63]301            $OID_NUMBER{sysDescription},
[18]302            $OID_NUMBER{sysName},
303            $OID_NUMBER{sysContact},
304            $OID_NUMBER{sysLocation},
305            ]
[2]306         );
[18]307      $sw->{description} = $result->{$OID_NUMBER{sysName}} || $sw->{hostname};
[63]308      $sw->{model} = get_switch_model( $result->{$OID_NUMBER{sysDescription}});
[3]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};
[2]311      $session->close;
[22]312
[63]313      # Ligne à virer car on récupère maintenant le modèle du switch
314      my ($desc, $type) = split m/ : /xms, $sw->{description}, 2;
[20]315      printf "%-25s 0--------->>>> %-25s %s\n", $sw->{hostname}, $desc, $sw->{model} if $verbose;
[2]316      }
317
318   print "\n" if $verbose;
[63]319   return;
[2]320   }
321
322###
323# convertit l'hexa (uniquement 2 chiffres) en decimal
[4]324sub hex_to_dec {
[2]325   #00:0F:1F:43:E4:2B
[63]326   my $car = '00' . uc shift;
[2]327
[4]328   return '00' if $car eq '00UNKNOW';
[2]329   my %table = (
[62]330      '0'=>'0',  '1'=>'1',  '2'=>'2',  '3'=>'3',  '4'=>'4',
331      '5'=>'5',  '6'=>'6',  '7'=>'7',  '8'=>'8',  '9'=>'9',
[63]332      'A'=>'10', 'B'=>'11', 'C'=>'12', 'D'=>'13', 'E'=>'14', 'F'=>'15',
[2]333      );
[63]334   my @chars = split m//xms, $car;
[2]335   return $table{$chars[-2]}*16 + $table{$chars[-1]};
336   }
337
338###
339# convertit l'@ arp en decimal
[4]340sub arp_hex_to_dec {
[2]341   #00:0F:1F:43:E4:2B
342   my $arp = shift;
343
[63]344   my @paquets = split m/ : /xms, $arp;
345   my $return = q{};
[2]346   foreach(@paquets) {
[63]347      $return .= q{.} . hex_to_dec($_);
[2]348      }
349   return $return;
350   }
351
352###
353# va rechercher le port et le switch sur lequel est la machine
354sub find_switch_port {
[63]355   my $arp             = shift;
356   my $switch_proposal = shift || q{};
357
[2]358   my %ret;
[63]359   $ret{switch_description} = 'unknow';
360   $ret{switch_port} = '0';
[2]361
362   return %ret if $arp eq 'unknow';;
363
[22]364   my @switch_search = @SWITCH;
[63]365   if ($switch_proposal ne q{}) {
[11]366      for my $sw (@SWITCH) {
[2]367         next if $sw->{hostname} ne $switch_proposal;
[22]368         unshift @switch_search, $sw;
[2]369         last;
370         }
371      }
372
[63]373   my $research = '1.3.6.1.2.1.17.4.3.1.2' . arp_hex_to_dec($arp);
374
[2]375   LOOP_ON_SWITCH:
[22]376   for my $sw (@switch_search) {
[3]377      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
[22]378      print "$error \n" if $error;
379
[2]380      my $result = $session->get_request(
381         -varbindlist => [$research]
382         );
[63]383      if (not defined $result or $result->{$research} eq 'noSuchInstance') {
[2]384         $session->close;
385         next LOOP_ON_SWITCH;
386         }
387
388         my $swport = $result->{$research};
389         $session->close;
390
391         # IMPORTANT !!
[63]392         # ceci empeche la detection sur certains port ...
[2]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}}) {
[22]400            next SWITCH_PORT_IGNORE if $swport ne get_numerical_port($sw->{model},$p);
[2]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};
[22]407            $ret{switch_port}        = get_human_readable_port($sw->{model}, $swport); # $swport;
[63]408
[2]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
[11]426   for my $sw (@SWITCH) {
[15]427      $SWITCH_PORT_COUNT{$sw->{hostname}} = {} if not exists $SWITCH_PORT_COUNT{$sw->{hostname}};
[2]428      }
429
[63]430   my $research = '1.3.6.1.2.1.17.4.3.1.2' . arp_hex_to_dec($arp);
[2]431   LOOP_ON_ALL_SWITCH:
[11]432   for my $sw (@SWITCH) {
[3]433      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
[13]434      print "$error \n" if $error;
435
[2]436      my $result = $session->get_request(
437         -varbindlist => [$research]
438         );
[13]439
[63]440      if(defined $result and $result->{$research} ne 'noSuchInstance'){
[2]441         my $swport = $result->{$research};
442
[68]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
[2]449         $ret->{$sw->{hostname}} = {};
450         $ret->{$sw->{hostname}}{hostname}    = $sw->{hostname};
451         $ret->{$sw->{hostname}}{description} = $sw->{description};
[22]452         $ret->{$sw->{hostname}}{port}        = get_human_readable_port($sw->{model}, $swport);
[2]453
[15]454         $SWITCH_PORT_COUNT{$sw->{hostname}}->{$swport}++;
[2]455         }
456
457      $session->close;
458      }
459   return $ret;
460   }
461
462sub get_list_network {
463
[12]464   return keys %{$KLASK_CFG->{network}};
[2]465   }
466
467sub get_current_interface {
468   my $network = shift;
469
[12]470   return $KLASK_CFG->{network}{$network}{interface};
[2]471   }
[63]472
[2]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) {
[12]481      my @line  = @{$KLASK_CFG->{network}{$net}{'ip-subnet'}};
[2]482      for my $cmd (@line) {
[63]483         for my $method (keys %{$cmd}){
[2]484            $cidrlist->add_any($cmd->{$method}) if $method eq 'add';
485            }
486         }
487      }
488
[4]489   my @res = ();
[2]490
491   for my $cidr ($cidrlist->list()) {
492      my $net = new NetAddr::IP $cidr;
[63]493      for my $ip (@{$net}) {
494         $ip =~ s{ /32 }{}xms;
[2]495         push @res,  $ip;
496         }
497      }
498
499   return @res;
500   }
501
[9]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) {
[12]509      push @res, $KLASK_CFG->{network}{$net}{'main-router'};
[9]510      }
511
512   return @res;
513   }
514
[2]515sub get_human_readable_port {
[22]516   my $sw_model = shift;
517   my $sw_port  = shift;
[63]518
[64]519   if ($sw_model eq 'HP8000M') {
[63]520
[64]521      my $reste = (($sw_port - 1) % 8) + 1;
522      my $major = int (($sw_port - 1) / 8);
523      return "$INTERNAL_PORT_MAP{$major}$reste";
524      }
[2]525
[64]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;
[2]543   }
544
545sub get_numerical_port {
[22]546   my $sw_model = shift;
547   my $sw_port  = shift;
[63]548
[64]549   if ($sw_model eq 'HP8000M') {
[2]550
[64]551      my $letter = substr $sw_port, 0, 1;
552      my $reste =  substr $sw_port, 1;
[63]553
[64]554      return $INTERNAL_PORT_MAP_REV{$letter} * 8 + $reste;
555      }
[63]556
[64]557   if ($sw_model eq 'HP2424M') {
558      if ($sw_port =~ m/^A/xms ) {
[63]559
[64]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;
[2]576   }
577
578################
579# Les commandes
580################
581
582sub cmd_help {
583
[63]584print <<'END';
[2]585klask - ports manager and finder for switch
586
587 klask updatedb
[69]588 klask exportdb --format [txt|html]
[2]589
[45]590 klask updatesw
[69]591 klask exportsw --format [txt|dot]
[45]592
[2]593 klask searchdb computer
594 klask search   computer
[68]595 klask search-mac-on-switch switch mac_addr
[2]596
[69]597 klask ip-free --day number_of_day [vlan_name]
598
[2]599 klask enable  switch port
600 klask disable switch port
601 klask status  switch port
602END
[63]603   return;
[2]604   }
605
[36]606sub cmd_version {
607
[63]608print <<'END';
[36]609Klask - ports manager and finder for switch
610Copyright (C) 2005-2008 Gabriel Moreau
611
612END
[37]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";
[63]616   return;
[36]617   }
618
[2]619sub cmd_search {
620   my @computer = @_;
[63]621
[4]622   init_switch_names();    #nomme les switchs
[2]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
[63]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"
[2]628         unless $where{switch_description} eq 'unknow' and $resol_arp{hostname_fq} eq 'unknow' and $resol_arp{mac_address} eq 'unknow';
629      }
[63]630   return;
[2]631   }
632
633sub cmd_searchdb {
634   my @computer = @_;
635
636   fastping(@computer);
[44]637   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
[63]638
[2]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};
[63]643
[2]644      next LOOP_ON_COMPUTER unless exists $computerdb->{$ip};
[63]645
646      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
[2]647      $year += 1900;
648      $mon++;
[63]649      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
[2]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      }
[63]659   return;
[2]660   }
661
662sub cmd_updatedb {
663   my @network = @_;
664      @network = get_list_network() if not @network;
665
[34]666   test_switchdb_environnement();
667
668   my $computerdb = {};
[44]669      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
[2]670   my $timestamp = time;
[22]671
[2]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;
[63]676   my $size_of_database   = keys %{$computerdb};
[31]677      $size_of_database   = 1 if $size_of_database == 0;
[2]678   my $i = 0;
679   my $detected_computer = 0;
[22]680
[4]681   init_switch_names('yes');    #nomme les switchs
[2]682
[22]683   { # Remplis le champs portignore des ports d'inter-connection pour chaque switch
[44]684   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
[22]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 = ();
[63]688   for my $swport (keys %db_switch_connected_on_port) {
689      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[22]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}};
[63]696         push @{$sw->{portignore}}, split m/ : /xms, $db_switch_chained_port{$sw->{hostname}};
[22]697         }
698#      print "$sw->{hostname} ++ @{$sw->{portignore}}\n";
699      }
700   }
701
[2]702   my %router_mac_ip = ();
703   DETECT_ALL_ROUTER:
[9]704#   for my $one_router ('194.254.66.254') {
705   for my $one_router ( get_list_main_router(@network) ) {
[2]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++;
[49]721
[63]722         my $total_percent = int (($i*100)/$number_of_computer);
723
[2]724         my $localtime = time - $timestamp;
[63]725         my ($sec,$min) = localtime $localtime;
[2]726
727         my $time_elapse = 0;
728            $time_elapse = $localtime * ( 100 - $total_percent) / $total_percent if $total_percent != 0;
[63]729         my ($sec_elapse,$min_elapse) = localtime $time_elapse;
[2]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);
[63]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;
[2]737
738         my %resol_arp = resolve_ip_arp_host($one_computer,$current_interface);
[63]739
[9]740         # do not search on router connection (why ?)
[2]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
[9]746         # do not search on switch inter-connection
[2]747         if (exists $switch_level{$resol_arp{hostname_fq}}) {
748            $computer_not_detected{$one_computer} = $current_interface;
749            next LOOP_ON_COMPUTER;
750            }
751
[63]752         my $switch_proposal = q{};
[2]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
[3]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
[2]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
[3]773#         if ($resol_arp{mac_address} eq 'unknow') {
774#            $computer_not_detected{$one_computer} = $current_interface;
775#            next LOOP_ON_COMPUTER;
776#            }
[2]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,
[45]788               network            => $net,
[2]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},
[45]803               network            => $net,
[2]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};
[63]809
[2]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;
[63]816
[2]817         }
818      }
819
820   # final end of line at the end of the loop
821   printf "\n";
822
[13]823   my $dirdb = $KLASK_DB_FILE;
[63]824      $dirdb =~ s{ / [^/]* $}{}xms;
[2]825   mkdir "$dirdb", 0755 unless -d "$dirdb";
[44]826   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
[2]827
828   for my $one_computer (keys %computer_not_detected) {
829      my $interface = $computer_not_detected{$one_computer};
[16]830      system "arping -c 1 -w 1 -rR -i $interface $one_computer &>/dev/null";
[2]831#      print  "arping -c 1 -w 1 -rR -i $interface $one_computer 2>/dev/null\n";
832      }
[63]833   return;
[2]834   }
835
836sub cmd_removedb {
837   my @computer = @_;
[34]838
839   test_maindb_environnement();
840
[44]841   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
[2]842
843   LOOP_ON_COMPUTER:
844   for my $one_computer (@computer) {
845
[66]846      if ( $one_computer =~ m/^ $RE_IPv4_ADDRESS $/xms
847            and exists $computerdb->{$one_computer} ) {
848         delete $computerdb->{$one_computer};
849         next;
850         }
851
[2]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
[13]857   my $dirdb = $KLASK_DB_FILE;
[63]858      $dirdb =~ s{ / [^/]* $}{}xms;
[2]859   mkdir "$dirdb", 0755 unless -d "$dirdb";
[44]860   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
[63]861   return;
[2]862   }
863
864sub cmd_exportdb {
[45]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};
[63]879
[45]880   $possible_format{$format}->(@ARGV);
[63]881   return;
[45]882   }
883
884sub cmd_exportdb_txt {
[34]885   test_maindb_environnement();
886
[44]887   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
[2]888
[70]889   printf "%-25s %-4s            %-40s %-15s %-18s %-16s %s\n", qw(Switch Port Hostname IPv4-Address MAC-Address Date VLAN);
890   print "-------------------------------------------------------------------------------------------------------------------------------------------\n";
[2]891
892   LOOP_ON_IP_ADDRESS:
[63]893   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
894
[2]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';
[63]902
903      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
[2]904      $year += 1900;
905      $mon++;
[63]906      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
[2]907
[70]908      printf "%-26s  %2s  <-------  %-40s %-15s %-18s %-16s %s\n",
[2]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},
[70]914         $date,
915         $computerdb->{$ip}{network} || '';
[2]916      }
[63]917   return;
[2]918   }
919
[45]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
[63]928   print <<'END_HTML';
[45]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>
[67]938   <th scope="col" class="sorttable_alpha">MAC-Address</th>
[45]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>
[63]954END_HTML
[45]955
956   my %mac_count = ();
957   LOOP_ON_IP_ADDRESS:
[63]958   foreach my $ip (keys %{$computerdb}) {
959
[45]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 !
[63]962
[45]963      $mac_count{$computerdb->{$ip}{mac_address}}++;
964      }
965
966   my $typerow = 'even';
967
968   LOOP_ON_IP_ADDRESS:
[63]969   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
970
[45]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
[63]974      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
[45]975      $year += 1900;
976      $mon++;
[63]977      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
[45]978
979#      $odd_or_even++;
980#      my $typerow = $odd_or_even % 2 ? 'odd' : 'even';
[63]981      $typerow = $typerow eq 'even' ? 'odd' : 'even';
[45]982
983      my $switch_hostname = $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description} || 'unkown';
984      chomp $switch_hostname;
[63]985      my $switch_hostname_sort = sprintf '%s %3s' ,$switch_hostname, $computerdb->{$ip}{switch_port};
[45]986
[63]987      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
[45]988
[63]989      my $mac_sort = sprintf '%04i-%s', 9999 - $mac_count{$computerdb->{$ip}{mac_address}}, $computerdb->{$ip}{mac_address};
[45]990
[63]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};
[45]993
[63]994      print <<"END_HTML";
[45]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>
[63]1004END_HTML
[45]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
[63]1016      my $switch_hostname_sort = sprintf '%s %3s' ,$sw, $db_switch_output_port{$sw};
[45]1017
[63]1018      $typerow = $typerow eq 'even' ? 'odd' : 'even';
[45]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++;
[63]1029      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
[45]1030
[63]1031      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
[45]1032
[63]1033      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
[45]1034
[63]1035      my ( $host_short ) = sprintf '%s %3s' , split(m/ \. /xms, $db_switch_parent{$sw}->{switch}, 1), $db_switch_parent{$sw}->{port};
[45]1036
[63]1037      print <<"END_HTML";
[45]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>
[63]1047END_HTML
[45]1048         }
1049      else {
[63]1050         print <<"END_HTML";
[45]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>
[63]1060END_HTML
[45]1061         }
1062      }
1063
1064   for my $swport (sort keys %db_switch_connected_on_port) {
[63]1065      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[45]1066      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1067
[63]1068         my $switch_hostname_sort = sprintf '%s %3s' ,$sw_connect, $port_connect;
[45]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++;
[63]1077      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year,$mon,$mday,$hour,$min;
[45]1078
[63]1079      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
[45]1080
[63]1081      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
[45]1082
[63]1083      $typerow = $typerow eq 'even' ? 'odd' : 'even';
[45]1084
1085         if (exists $db_switch_output_port{$sw}) {
1086
[63]1087            my ( $host_short ) = sprintf '%s %3s' , split( m/\./xms, $sw, 1), $db_switch_output_port{$sw};
[45]1088
[63]1089            print <<"END_HTML";
[45]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>
[63]1099END_HTML
[45]1100            }
1101         else {
[63]1102            print <<"END_HTML";
[45]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>
[63]1112END_HTML
[45]1113            }
1114         }
1115      }
1116
[63]1117   print <<'END_HTML';
[45]1118 </tbody>
1119</table>
[63]1120END_HTML
1121   return;
[45]1122   }
1123
[2]1124sub cmd_iplocation {
[44]1125   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
[2]1126
1127   LOOP_ON_IP_ADDRESS:
[63]1128   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
[2]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
[63]1132      my $sw_hostname = $computerdb->{$ip}{switch_hostname} || q{};
[2]1133      next if $sw_hostname eq 'unknow';
[63]1134
1135      my $sw_location = q{};
[11]1136      for my $sw (@SWITCH) {
[2]1137         next if $sw_hostname ne $sw->{hostname};
1138         $sw_location = $sw->{location};
1139         last;
1140         }
1141
[63]1142      printf "%s: \"%s\"\n", $ip, $sw_location if not $sw_location eq q{};
[2]1143      }
[63]1144   return;
[2]1145   }
1146
[69]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
[2]1218sub cmd_enable {
1219   my $switch = shift;
1220   my $port   = shift;
[63]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)
[2]1224   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
[63]1225   return;
[2]1226   }
1227
1228sub cmd_disable {
1229   my $switch = shift;
1230   my $port   = shift;
[63]1231
[2]1232   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
[63]1233   return;
[2]1234   }
1235
1236sub cmd_status {
1237   my $switch = shift;
1238   my $port   = shift;
[63]1239
[2]1240   system "snmpget -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port";
[63]1241   return;
[2]1242   }
1243
[35]1244sub cmd_search_mac_on_switch {
[63]1245   my $switch_name = shift || q{};
1246   my $mac_address = shift || q{};
1247
1248   if ($switch_name eq q{} or $mac_address eq q{}) {
[39]1249      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
[38]1250      }
[39]1251
1252   if (not defined $SWITCH_DB{$switch_name}) {
[63]1253      die "Switch $switch_name must be defined in klask configuration file\n";
[39]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;
[63]1260   if (exists $sw->{version} and $sw->{version} eq '3') {
[39]1261      $session{-username} = $sw->{username} || 'snmpadmin';
1262      }
1263   else {
1264      $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1265      }
1266
[63]1267   my $research = '1.3.6.1.2.1.17.4.3.1.2' . arp_hex_to_dec($mac_address);
[35]1268   print "Klask search OID $research on switch $switch_name\n";
[2]1269
[39]1270   my ($session, $error) = Net::SNMP->session( %session );
[35]1271   print "$error \n" if $error;
1272
1273   my $result = $session->get_request(
1274      -varbindlist => [$research]
1275      );
[63]1276
1277   if (not defined $result or $result->{$research} eq 'noSuchInstance') {
[35]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";
[63]1286   return;
[35]1287   }
1288
[4]1289sub cmd_updatesw {
[2]1290
[4]1291   init_switch_names('yes');    #nomme les switchs
[2]1292   print "\n";
1293
1294   my %where = ();
1295   my %db_switch_output_port = ();
1296   my %db_switch_ip_hostname = ();
1297
1298   DETECT_ALL_ROUTER:
[9]1299#   for my $one_computer ('194.254.66.254') {
[11]1300   for my $one_router ( get_list_main_router(get_list_network()) ) {
[63]1301      my %resol_arp = resolve_ip_arp_host($one_router, q{*}, q{low}); # resolution arp
[2]1302      next DETECT_ALL_ROUTER if $resol_arp{mac_address} eq 'unknow';
[13]1303      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # retrouve les emplacements des routeurs
[2]1304      }
1305
1306   ALL_ROUTER_IP_ADDRESS:
[13]1307   for my $ip (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
[63]1308
[13]1309      next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip}; # /a priori/ idiot car ne sers à rien...
[2]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';
[63]1317
[2]1318         $db_switch_output_port{$switch->{hostname}} = $switch->{port};
1319         }
[63]1320      }
[2]1321
1322   my %db_switch_link_with = ();
1323
1324   my @list_switch_ip = ();
1325   my @list_switch_ipv4 = ();
[11]1326   for my $sw (@SWITCH){
[2]1327      push @list_switch_ip, $sw->{hostname};
1328      }
1329
[45]1330   my $timestamp = time;
1331
[2]1332   ALL_SWITCH:
1333   for my $one_computer (@list_switch_ip) {
[63]1334      my %resol_arp = resolve_ip_arp_host($one_computer, q{*}, q{low}); # arp resolution
[2]1335      next ALL_SWITCH if $resol_arp{mac_address} eq 'unknow';
[63]1336
[2]1337      push @list_switch_ipv4,$resol_arp{ipv4_address};
[63]1338
[2]1339      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # find port on all switch
[68]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";
[2]1345
1346      $db_switch_ip_hostname{$resol_arp{ipv4_address}} = $resol_arp{hostname_fq};
[45]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;
[2]1351      }
[63]1352
[2]1353   ALL_SWITCH_IP_ADDRESS:
1354   for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
[63]1355
[2]1356      next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
1357
1358      DETECTED_SWITCH:
1359      for my $switch_detected ( keys %{$where{$ip}} ) {
1360
[15]1361         next DETECTED_SWITCH if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostname{$ip}};
[2]1362
1363         my $switch = $where{$ip}->{$switch_detected};
[68]1364#print "DEBUG1 :  $db_switch_ip_hostname{$ip} / $switch->{hostname} : $switch->{port}\n" if  $switch->{hostname} =~ m/sw3-batA0-3s/;
[2]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};
[68]1372#print "DEBUG :  $db_switch_ip_hostname{$ip} / $switch->{hostname} : $switch->{port}\n" if $switch->{hostname} !~ m/sw3-batA0-3s/;
[2]1373         }
1374
1375      }
[63]1376
[2]1377   my %db_switch_connected_on_port = ();
1378   my $maybe_more_than_one_switch_connected = 'yes';
[63]1379
[2]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}}) {
[63]1383
[2]1384            my $port = $db_switch_link_with{$sw}->{$connect};
[63]1385
[2]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) {
[63]1395
[2]1396         next if keys %{$db_switch_connected_on_port{$swport}} == 1;
[63]1397
[2]1398         $maybe_more_than_one_switch_connected = 'yes';
1399
[63]1400         my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]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) {
[63]1405
[2]1406            next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
[63]1407
[2]1408            $db_switch_connected_on_port{$swport} = {$sw_connected => 1};
[63]1409
[2]1410            for my $other_sw (@sw_on_same_port) {
1411               next if $other_sw eq $sw_connected;
[63]1412
[2]1413               delete $db_switch_link_with{$other_sw}->{$sw_connect};
1414               }
[63]1415
[2]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}}) {
[63]1426
[2]1427         my $port = $db_switch_link_with{$sw}->{$connect};
[63]1428
[2]1429         $db_switch_connected_on_port{"$connect:$port"} ||= {};
1430         $db_switch_connected_on_port{"$connect:$port"}->{$sw} = $port;
[63]1431
[2]1432         $db_switch_parent{$sw} = {switch => $connect, port => $port};
1433         }
1434      }
1435
[63]1436   print "Switch output port and parent port connection\n";
[2]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";
[63]1450   for my $swport (sort keys %db_switch_connected_on_port) {
1451      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]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,
[25]1467      switch_db         => \%SWITCH_DB,
[2]1468      };
[63]1469
[44]1470   YAML::Syck::DumpFile("$KLASK_SW_FILE", $switch_connection);
[63]1471   return;
[2]1472   }
1473
[4]1474sub cmd_exportsw {
1475   my @ARGV   = @_;
[2]1476
[34]1477   test_switchdb_environnement();
1478
[4]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};
[63]1491
[4]1492   $possible_format{$format}->(@ARGV);
[63]1493   return;
[4]1494   }
1495
1496sub cmd_exportsw_txt {
1497
[44]1498   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
[4]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
[63]1504   print "Switch output port and parent port connection\n";
[4]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";
[63]1518   for my $swport (sort keys %db_switch_connected_on_port) {
1519      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[4]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      }
[63]1529   return;
[4]1530   }
1531
1532sub cmd_exportsw_dot {
1533
[44]1534   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
[63]1535
[2]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}};
[25]1540   my %db_switch_global            = %{$switch_connection->{switch_db}};
1541
[2]1542   my %db_building= ();
[11]1543   for my $sw (@SWITCH) {
[63]1544      my ($building, $location) = split m/ \/ /xms, $sw->{location}, 2;
[2]1545      $db_building{$building} ||= {};
1546      $db_building{$building}->{$location} ||= {};
1547      $db_building{$building}->{$location}{ $sw->{hostname} } = 'y';
1548      }
[63]1549
1550
[2]1551   print "digraph G {\n";
1552
[4]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";
[2]1555
1556   my $b=0;
1557   for my $building (keys %db_building) {
1558      $b++;
[63]1559
[4]1560      print "\"building$b\" [label = \"$building\", color = black, fillcolor = gold, style = filled];\n";
1561      print "site -> \"building$b\" [len = 2, color = firebrick];\n";
[2]1562
1563      my $l = 0;
1564      for my $loc (keys %{$db_building{$building}}) {
1565         $l++;
[63]1566
1567         print "\"location$b-$l\" [label = \"$building" . q{/} . join(q{\n}, split(m{ / }xms, $loc)) . "\", color = black, fillcolor = orange, style = filled];\n";
[33]1568#         print "\"location$b-$l\" [label = \"$building / $loc\", color = black, fillcolor = orange, style = filled];\n";
[4]1569         print "\"building$b\" -> \"location$b-$l\" [len = 2, color = firebrick]\n";
[2]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
[25]1575            my $swname  = $sw;
[63]1576               $swname .= q{\n-\n} . "$db_switch_global{$sw}->{model}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{model};
[25]1577            print "\"$sw\" [label = \"$swname\", color = black, fillcolor = palegreen, shape = rect, style = filled];\n";
[4]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";
[2]1580
1581
1582            for my $swport (keys %db_switch_connected_on_port) {
[63]1583               my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]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";
[4]1587               print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, arrowhead= normal, arrowtail = inv]\n";
[2]1588              }
1589            }
1590         }
1591      }
1592
[63]1593#   print "Switch output port and parent port connection\n";
[2]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";
[63]1607   for my $swport (sort keys %db_switch_connected_on_port) {
1608      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]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";
[63]1620   return;
[2]1621   }
1622
1623
1624__END__
1625
1626=head1 NAME
1627
1628klask - ports manager and finder for switch
1629
1630
[63]1631=head1 USAGE
[2]1632
1633 klask updatedb
[45]1634 klask exportdb --format [txt|html]
[2]1635
[5]1636 klask updatesw
1637 klask exportsw --format [txt|dot]
1638
[2]1639 klask searchdb computer
1640 klask search   computer
[69]1641 klask search-mac-on-switch switch mac_addr
[2]1642
[69]1643 klask ip-free --day number_of_day [vlan_name]
1644
[2]1645 klask enable  switch port
1646 klask disable swith port
1647 klask status  swith port
1648
1649
1650=head1 DESCRIPTION
1651
[6]1652klask is a small tool to find where is a host in a big network. klask mean search in brittany.
[2]1653
[6]1654Klask has now a web site dedicated for it !
[2]1655
[6]1656 http://servforge.legi.inpg.fr/projects/klask
1657
1658
[2]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
[5]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
[2]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:
[5]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
[2]1723
[5]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
[2]1731 switch:
1732   - hostname: sw1.klask.local
1733     portignore:
1734       - 1
1735       - 2
1736
1737   - hostname: sw2.klask.local
[5]1738     location: BatK / 2 / K203
1739     type: HP2424
[2]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
[7]1751 /var/cache/klask/switchdb
[2]1752
1753=head1 SEE ALSO
1754
1755Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
1756
1757
1758=head1 VERSION
1759
[36]1760$Id: klask 70 2010-11-02 15:55:37Z g7moreau $
[2]1761
1762
1763=head1 AUTHOR
1764
[5]1765Written by Gabriel Moreau, Grenoble - France
[2]1766
1767
[63]1768=head1 LICENSE AND COPYRIGHT
[2]1769
1770GPL version 2 or later and Perl equivalent
[45]1771
[63]1772Copyright (C) 2005-2009 Gabriel Moreau.
Note: See TracBrowser for help on using the repository browser.