source: trunk/klask @ 69

Last change on this file since 69 was 69, checked in by g7moreau, 14 years ago
  • Add command cmd_ip_free

klask ip-free

return ip not use in one or more VLAN.

  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Id Rev
File size: 57.8 KB
RevLine 
[2]1#!/usr/bin/perl -w
[32]2#
3# Copyright (C) 2005-2008 Gabriel Moreau.
4#
5# $Id: klask 69 2010-11-02 15:43:26Z 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: 69 $'."\n";
614   print ' $Date: 2010-11-02 15:43:26 +0000 (Tue, 02 Nov 2010) $'."\n";
615   print ' $Id: klask 69 2010-11-02 15:43:26Z 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
889   printf "%-24s %-4s            %-30s %-15s %-18s %-s\n", qw(Switch Port Hostname IPv4-Address MAC-Address Date);
890   print "---------------------------------------------------------------------------------------------------------------------------\n";
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
908      printf "%-25s  %2s  <-------  %-30s %-15s %-18s %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      }
[63]916   return;
[2]917   }
918
[45]919sub cmd_exportdb_html {
920   test_maindb_environnement();
921
922   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
923
924#<link rel="stylesheet" type="text/css" href="style-klask.css" />
925#<script src="sorttable-klask.js"></script>
926
[63]927   print <<'END_HTML';
[45]928<table class="sortable" summary="Klask export database">
929 <caption>Klask database</caption>
930 <thead>
931  <tr>
932   <th scope="col" class="hklask-switch">Switch</th>
933   <th scope="col" class="sorttable_nosort">Port</th>
934   <th scope="col" class="sorttable_nosort">Link</th>
935   <th scope="col" class="sorttable_alpha">Hostname</th>
936   <th scope="col" class="hklask-ipv4">IPv4-Address</th>
[67]937   <th scope="col" class="sorttable_alpha">MAC-Address</th>
[45]938   <th scope="col" class="hklask-date">Date</th>
939  </tr>
940 </thead>
941 <tfoot>
942  <tr>
943   <th scope="col" class="fklask-switch">Switch</th>
944   <th scope="col" class="fklask-port">Port</th>
945   <th scope="col" class="fklask-link">Link</th>
946   <th scope="col" class="fklask-hostname">Hostname</th>
947   <th scope="col" class="fklask-ipv4">IPv4-Address</th>
948   <th scope="col" class="fklask-mac">MAC-Address</th>
949   <th scope="col" class="fklask-date">Date</th>
950  </tr>
951 </tfoot>
952 <tbody>
[63]953END_HTML
[45]954
955   my %mac_count = ();
956   LOOP_ON_IP_ADDRESS:
[63]957   foreach my $ip (keys %{$computerdb}) {
958
[45]959      # to be improve in the future
960      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
[63]961
[45]962      $mac_count{$computerdb->{$ip}{mac_address}}++;
963      }
964
965   my $typerow = 'even';
966
967   LOOP_ON_IP_ADDRESS:
[63]968   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
969
[45]970      # to be improve in the future
971      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
972
[63]973      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
[45]974      $year += 1900;
975      $mon++;
[63]976      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
[45]977
978#      $odd_or_even++;
979#      my $typerow = $odd_or_even % 2 ? 'odd' : 'even';
[63]980      $typerow = $typerow eq 'even' ? 'odd' : 'even';
[45]981
982      my $switch_hostname = $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description} || 'unkown';
983      chomp $switch_hostname;
[63]984      my $switch_hostname_sort = sprintf '%s %3s' ,$switch_hostname, $computerdb->{$ip}{switch_port};
[45]985
[63]986      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
[45]987
[63]988      my $mac_sort = sprintf '%04i-%s', 9999 - $mac_count{$computerdb->{$ip}{mac_address}}, $computerdb->{$ip}{mac_address};
[45]989
[63]990      $computerdb->{$ip}{hostname_fq} = 'unknow' if $computerdb->{$ip}{hostname_fq} =~ m/^ \d+ \. \d+ \. \d+ \. \d+ $/xms;
991      my ( $host_short ) = split m/ \. /xms, $computerdb->{$ip}{hostname_fq};
[45]992
[63]993      print <<"END_HTML";
[45]994  <tr class="$typerow">
995   <td sorttable_customkey="$switch_hostname_sort">$switch_hostname</td>
996   <td class="bklask-port">$computerdb->{$ip}{switch_port}</td>
997   <td><-------</td>
998   <td sorttable_customkey="$host_short">$computerdb->{$ip}{hostname_fq}</td>
999   <td sorttable_customkey="$ip_sort">$ip</td>
1000   <td sorttable_customkey="$mac_sort">$computerdb->{$ip}{mac_address}</td>
1001   <td>$date</td>
1002  </tr>
[63]1003END_HTML
[45]1004      }
1005
1006   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1007
1008   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1009   my %db_switch_parent            = %{$switch_connection->{parent}};
1010   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1011   my %db_switch                   = %{$switch_connection->{switch_db}};
1012
1013   for my $sw (sort keys %db_switch_output_port) {
1014
[63]1015      my $switch_hostname_sort = sprintf '%s %3s' ,$sw, $db_switch_output_port{$sw};
[45]1016
[63]1017      $typerow = $typerow eq 'even' ? 'odd' : 'even';
[45]1018
1019      if (exists $db_switch_parent{$sw}) {
1020
1021      my $mac_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{mac_address};
1022      my $ipv4_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{ipv4_address};
1023      my $timestamp = $db_switch{$db_switch_parent{$sw}->{switch}}->{timestamp};
1024
1025      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1026      $year += 1900;
1027      $mon++;
[63]1028      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
[45]1029
[63]1030      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
[45]1031
[63]1032      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
[45]1033
[63]1034      my ( $host_short ) = sprintf '%s %3s' , split(m/ \. /xms, $db_switch_parent{$sw}->{switch}, 1), $db_switch_parent{$sw}->{port};
[45]1035
[63]1036      print <<"END_HTML";
[45]1037  <tr class="$typerow">
1038   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1039   <td class="bklask-port">$db_switch_output_port{$sw}</>
1040   <td>+--> $db_switch_parent{$sw}->{port}</td>
1041   <td sorttable_customkey="$host_short">$db_switch_parent{$sw}->{switch}</>
1042   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1043   <td sorttable_customkey="$mac_sort">$mac_address</td>
1044   <td>$date</td>
1045  </tr>
[63]1046END_HTML
[45]1047         }
1048      else {
[63]1049         print <<"END_HTML";
[45]1050  <tr class="$typerow">
1051   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1052   <td class="bklask-port">$db_switch_output_port{$sw}</>
1053   <td>+--></td>
1054   <td sorttable_customkey="router">router</>
1055   <td sorttable_customkey="999999999999"></td>
1056   <td sorttable_customkey="99999"></td>
1057   <td></td>
1058  </tr>
[63]1059END_HTML
[45]1060         }
1061      }
1062
1063   for my $swport (sort keys %db_switch_connected_on_port) {
[63]1064      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[45]1065      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1066
[63]1067         my $switch_hostname_sort = sprintf '%s %3s' ,$sw_connect, $port_connect;
[45]1068
1069      my $mac_address = $db_switch{$sw}->{mac_address};
1070      my $ipv4_address = $db_switch{$sw}->{ipv4_address};
1071      my $timestamp = $db_switch{$sw}->{timestamp};
1072
1073      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1074      $year += 1900;
1075      $mon++;
[63]1076      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year,$mon,$mday,$hour,$min;
[45]1077
[63]1078      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
[45]1079
[63]1080      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
[45]1081
[63]1082      $typerow = $typerow eq 'even' ? 'odd' : 'even';
[45]1083
1084         if (exists $db_switch_output_port{$sw}) {
1085
[63]1086            my ( $host_short ) = sprintf '%s %3s' , split( m/\./xms, $sw, 1), $db_switch_output_port{$sw};
[45]1087
[63]1088            print <<"END_HTML";
[45]1089  <tr class="$typerow">
1090   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1091   <td class="bklask-port">$port_connect</>
1092   <td>&lt;--+ $db_switch_output_port{$sw}</td>
1093   <td sorttable_customkey="$host_short">$sw</>
1094   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1095   <td sorttable_customkey="$mac_sort">$mac_address</td>
1096   <td>$date</td>
1097  </tr>
[63]1098END_HTML
[45]1099            }
1100         else {
[63]1101            print <<"END_HTML";
[45]1102  <tr class="$typerow">
1103   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1104   <td class="bklask-port">$port_connect</>
1105   <td>&lt;--+</td>
1106   <td sorttable_customkey="$sw">$sw</>
1107   <td sorttable_customkey="">$ipv4_address</td>
1108   <td sorttable_customkey="">$mac_address</td>
1109   <td>$date</td>
1110  </tr>
[63]1111END_HTML
[45]1112            }
1113         }
1114      }
1115
[63]1116   print <<'END_HTML';
[45]1117 </tbody>
1118</table>
[63]1119END_HTML
1120   return;
[45]1121   }
1122
[2]1123sub cmd_iplocation {
[44]1124   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
[2]1125
1126   LOOP_ON_IP_ADDRESS:
[63]1127   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
[2]1128
1129      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1130
[63]1131      my $sw_hostname = $computerdb->{$ip}{switch_hostname} || q{};
[2]1132      next if $sw_hostname eq 'unknow';
[63]1133
1134      my $sw_location = q{};
[11]1135      for my $sw (@SWITCH) {
[2]1136         next if $sw_hostname ne $sw->{hostname};
1137         $sw_location = $sw->{location};
1138         last;
1139         }
1140
[63]1141      printf "%s: \"%s\"\n", $ip, $sw_location if not $sw_location eq q{};
[2]1142      }
[63]1143   return;
[2]1144   }
1145
[69]1146sub cmd_ip_free {
1147   my @vlan_name = @_;
1148
1149   my $days_to_dead = 365 * 2;
1150   my $format = 'txt';
1151
1152   my $ret = GetOptionsFromArray(\@vlan_name,
1153      'day|d=i'      => \$days_to_dead,
1154      'format|f=s'   => \$format,
1155      );
1156
1157   @vlan_name = get_list_network() if not @vlan_name;
1158
1159#   my %possible_format = (
1160#      txt  => \&cmd_exportdb_txt,
1161#      html => \&cmd_exportdb_html,
1162#      );
1163#
1164#   $format = 'txt' if not defined $possible_format{$format};
1165#
1166#   $possible_format{$format}->(@ARGV);
1167
1168   my $computerdb = {};
1169      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
1170   my $timestamp = time;
1171
1172   my $timestamp_barrier = $timestamp - (3600 * 24 * $days_to_dead );
1173
1174   my %result_ip= ();
1175
1176   ALL_NETWORK:
1177   for my $vlan (@vlan_name) {
1178
1179      my @ip_list = get_list_ip($vlan);
1180#      my $current_interface = get_current_interface($vlan);
1181     
1182      LOOP_ON_IP_ADDRESS:
1183      for my $ip (@ip_list) {
1184
1185         next LOOP_ON_IP_ADDRESS if
1186            exists $computerdb->{$ip}
1187            && $computerdb->{$ip}{timestamp} > $timestamp_barrier;
1188
1189         my $ip_date_last_detection = '';
1190         if (exists $computerdb->{$ip}) {
1191            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1192            $year += 1900;
1193            $mon++;
1194            $ip_date_last_detection = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1195            }
1196
1197         $result_ip{$ip} ||= {};
1198         $result_ip{$ip}->{date_last_detection} = $ip_date_last_detection;
1199
1200         my $packed_ip = scalar gethostbyname($ip);
1201         my $hostname_fq = 'unknown';
1202            $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) || 'unknown' if defined $packed_ip;
1203         $result_ip{$ip}->{hostname_fq} = $hostname_fq;
1204         
1205         $result_ip{$ip}->{vlan} = $vlan;
1206         }
1207      }
1208
1209   printf "%-15s %-40s %-16s %s\n", qw(IPv4-Address Hostname-FQ Date VLAN);
1210   print "-------------------------------------------------------------------------------\n";
1211   LOOP_ON_RESULT:
1212   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1213         printf "%-15s %-40s %-16s %s\n", $ip, $result_ip{$ip}->{hostname_fq}, $result_ip{$ip}->{date_last_detection}, $result_ip{$ip}->{vlan};
1214      }
1215   }
1216
[2]1217sub cmd_enable {
1218   my $switch = shift;
1219   my $port   = shift;
[63]1220
1221   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
1222   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
[2]1223   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
[63]1224   return;
[2]1225   }
1226
1227sub cmd_disable {
1228   my $switch = shift;
1229   my $port   = shift;
[63]1230
[2]1231   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
[63]1232   return;
[2]1233   }
1234
1235sub cmd_status {
1236   my $switch = shift;
1237   my $port   = shift;
[63]1238
[2]1239   system "snmpget -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port";
[63]1240   return;
[2]1241   }
1242
[35]1243sub cmd_search_mac_on_switch {
[63]1244   my $switch_name = shift || q{};
1245   my $mac_address = shift || q{};
1246
1247   if ($switch_name eq q{} or $mac_address eq q{}) {
[39]1248      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
[38]1249      }
[39]1250
1251   if (not defined $SWITCH_DB{$switch_name}) {
[63]1252      die "Switch $switch_name must be defined in klask configuration file\n";
[39]1253      }
1254
1255   my $sw = $SWITCH_DB{$switch_name};
1256   my %session = ( -hostname => $sw->{hostname} );
1257      $session{-version} = $sw->{version}   || 1;
1258      $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
[63]1259   if (exists $sw->{version} and $sw->{version} eq '3') {
[39]1260      $session{-username} = $sw->{username} || 'snmpadmin';
1261      }
1262   else {
1263      $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1264      }
1265
[63]1266   my $research = '1.3.6.1.2.1.17.4.3.1.2' . arp_hex_to_dec($mac_address);
[35]1267   print "Klask search OID $research on switch $switch_name\n";
[2]1268
[39]1269   my ($session, $error) = Net::SNMP->session( %session );
[35]1270   print "$error \n" if $error;
1271
1272   my $result = $session->get_request(
1273      -varbindlist => [$research]
1274      );
[63]1275
1276   if (not defined $result or $result->{$research} eq 'noSuchInstance') {
[35]1277      print "Klask do not find MAC $mac_address on switch $switch_name\n";
1278      $session->close;
1279      }
1280
1281   my $swport = $result->{$research};
1282   $session->close;
1283
1284   print "Klask find MAC $mac_address on switch $switch_name port $swport\n";
[63]1285   return;
[35]1286   }
1287
[4]1288sub cmd_updatesw {
[2]1289
[4]1290   init_switch_names('yes');    #nomme les switchs
[2]1291   print "\n";
1292
1293   my %where = ();
1294   my %db_switch_output_port = ();
1295   my %db_switch_ip_hostname = ();
1296
1297   DETECT_ALL_ROUTER:
[9]1298#   for my $one_computer ('194.254.66.254') {
[11]1299   for my $one_router ( get_list_main_router(get_list_network()) ) {
[63]1300      my %resol_arp = resolve_ip_arp_host($one_router, q{*}, q{low}); # resolution arp
[2]1301      next DETECT_ALL_ROUTER if $resol_arp{mac_address} eq 'unknow';
[13]1302      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # retrouve les emplacements des routeurs
[2]1303      }
1304
1305   ALL_ROUTER_IP_ADDRESS:
[13]1306   for my $ip (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
[63]1307
[13]1308      next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip}; # /a priori/ idiot car ne sers à rien...
[2]1309
1310      ALL_SWITCH_CONNECTED:
1311      for my $switch_detected ( keys %{$where{$ip}} ) {
1312
1313         my $switch = $where{$ip}->{$switch_detected};
1314
1315         next ALL_SWITCH_CONNECTED if $switch->{port} eq '0';
[63]1316
[2]1317         $db_switch_output_port{$switch->{hostname}} = $switch->{port};
1318         }
[63]1319      }
[2]1320
1321   my %db_switch_link_with = ();
1322
1323   my @list_switch_ip = ();
1324   my @list_switch_ipv4 = ();
[11]1325   for my $sw (@SWITCH){
[2]1326      push @list_switch_ip, $sw->{hostname};
1327      }
1328
[45]1329   my $timestamp = time;
1330
[2]1331   ALL_SWITCH:
1332   for my $one_computer (@list_switch_ip) {
[63]1333      my %resol_arp = resolve_ip_arp_host($one_computer, q{*}, q{low}); # arp resolution
[2]1334      next ALL_SWITCH if $resol_arp{mac_address} eq 'unknow';
[63]1335
[2]1336      push @list_switch_ipv4,$resol_arp{ipv4_address};
[63]1337
[2]1338      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # find port on all switch
[68]1339#my @l = ();
1340#for my $c  ( keys %{$where{$resol_arp{ipv4_address}}} ) {
1341#push @l, "$c -+ "; #$where{$resol_arp{ipv4_address}}->{$c}{hostname};
1342#}
1343#print "DEBUG  $one_computer $resol_arp{ipv4_address} $resol_arp{mac_address} --- @l\n";
[2]1344
1345      $db_switch_ip_hostname{$resol_arp{ipv4_address}} = $resol_arp{hostname_fq};
[45]1346
1347      $SWITCH_DB{$one_computer}->{ipv4_address} = $resol_arp{ipv4_address};
1348      $SWITCH_DB{$one_computer}->{mac_address}  = $resol_arp{mac_address};
1349      $SWITCH_DB{$one_computer}->{timestamp}    = $timestamp;
[2]1350      }
[63]1351
[2]1352   ALL_SWITCH_IP_ADDRESS:
1353   for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
[63]1354
[2]1355      next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
1356
1357      DETECTED_SWITCH:
1358      for my $switch_detected ( keys %{$where{$ip}} ) {
1359
[15]1360         next DETECTED_SWITCH if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostname{$ip}};
[2]1361
1362         my $switch = $where{$ip}->{$switch_detected};
[68]1363#print "DEBUG1 :  $db_switch_ip_hostname{$ip} / $switch->{hostname} : $switch->{port}\n" if  $switch->{hostname} =~ m/sw3-batA0-3s/;
[2]1364
1365         next if $switch->{port}     eq '0';
1366         next if $switch->{port}     eq $db_switch_output_port{$switch->{hostname}};
1367         next if $switch->{hostname} eq $db_switch_ip_hostname{$ip}; # $computerdb->{$ip}{hostname};
1368
1369         $db_switch_link_with{ $db_switch_ip_hostname{$ip} } ||= {};
1370         $db_switch_link_with{ $db_switch_ip_hostname{$ip} }->{ $switch->{hostname} } = $switch->{port};
[68]1371#print "DEBUG :  $db_switch_ip_hostname{$ip} / $switch->{hostname} : $switch->{port}\n" if $switch->{hostname} !~ m/sw3-batA0-3s/;
[2]1372         }
1373
1374      }
[63]1375
[2]1376   my %db_switch_connected_on_port = ();
1377   my $maybe_more_than_one_switch_connected = 'yes';
[63]1378
[2]1379   while ($maybe_more_than_one_switch_connected eq 'yes') {
1380      for my $sw (keys %db_switch_link_with) {
1381         for my $connect (keys %{$db_switch_link_with{$sw}}) {
[63]1382
[2]1383            my $port = $db_switch_link_with{$sw}->{$connect};
[63]1384
[2]1385            $db_switch_connected_on_port{"$connect:$port"} ||= {};
1386            $db_switch_connected_on_port{"$connect:$port"}->{$sw}++; # Just to define the key
1387            }
1388         }
1389
1390      $maybe_more_than_one_switch_connected  = 'no';
1391
1392      SWITCH_AND_PORT:
1393      for my $swport (keys %db_switch_connected_on_port) {
[63]1394
[2]1395         next if keys %{$db_switch_connected_on_port{$swport}} == 1;
[63]1396
[2]1397         $maybe_more_than_one_switch_connected = 'yes';
1398
[63]1399         my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]1400         my @sw_on_same_port = keys %{$db_switch_connected_on_port{$swport}};
1401
1402         CONNECTED:
1403         for my $sw_connected (@sw_on_same_port) {
[63]1404
[2]1405            next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
[63]1406
[2]1407            $db_switch_connected_on_port{$swport} = {$sw_connected => 1};
[63]1408
[2]1409            for my $other_sw (@sw_on_same_port) {
1410               next if $other_sw eq $sw_connected;
[63]1411
[2]1412               delete $db_switch_link_with{$other_sw}->{$sw_connect};
1413               }
[63]1414
[2]1415            # We can not do better for this switch for this loop
1416            next SWITCH_AND_PORT;
1417            }
1418         }
1419      }
1420
1421   my %db_switch_parent =();
1422
1423   for my $sw (keys %db_switch_link_with) {
1424      for my $connect (keys %{$db_switch_link_with{$sw}}) {
[63]1425
[2]1426         my $port = $db_switch_link_with{$sw}->{$connect};
[63]1427
[2]1428         $db_switch_connected_on_port{"$connect:$port"} ||= {};
1429         $db_switch_connected_on_port{"$connect:$port"}->{$sw} = $port;
[63]1430
[2]1431         $db_switch_parent{$sw} = {switch => $connect, port => $port};
1432         }
1433      }
1434
[63]1435   print "Switch output port and parent port connection\n";
[2]1436   print "---------------------------------------------\n";
1437   for my $sw (sort keys %db_switch_output_port) {
1438      if (exists $db_switch_parent{$sw}) {
1439         printf "%-25s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1440         }
1441      else {
1442         printf "%-25s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1443         }
1444      }
1445   print "\n";
1446
1447   print "Switch parent and children port inter-connection\n";
1448   print "------------------------------------------------\n";
[63]1449   for my $swport (sort keys %db_switch_connected_on_port) {
1450      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]1451      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1452         if (exists $db_switch_output_port{$sw}) {
1453            printf "%-25s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1454            }
1455         else {
1456            printf "%-25s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1457            }
1458         }
1459      }
1460
1461   my $switch_connection = {
1462      output_port       => \%db_switch_output_port,
1463      parent            => \%db_switch_parent,
1464      connected_on_port => \%db_switch_connected_on_port,
1465      link_with         => \%db_switch_link_with,
[25]1466      switch_db         => \%SWITCH_DB,
[2]1467      };
[63]1468
[44]1469   YAML::Syck::DumpFile("$KLASK_SW_FILE", $switch_connection);
[63]1470   return;
[2]1471   }
1472
[4]1473sub cmd_exportsw {
1474   my @ARGV   = @_;
[2]1475
[34]1476   test_switchdb_environnement();
1477
[4]1478   my $format = 'txt';
1479
1480   my $ret = GetOptions(
1481      'format|f=s'  => \$format,
1482      );
1483
1484   my %possible_format = (
1485      txt => \&cmd_exportsw_txt,
1486      dot => \&cmd_exportsw_dot,
1487      );
1488
1489   $format = 'txt' if not defined $possible_format{$format};
[63]1490
[4]1491   $possible_format{$format}->(@ARGV);
[63]1492   return;
[4]1493   }
1494
1495sub cmd_exportsw_txt {
1496
[44]1497   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
[4]1498
1499   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1500   my %db_switch_parent            = %{$switch_connection->{parent}};
1501   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1502
[63]1503   print "Switch output port and parent port connection\n";
[4]1504   print "---------------------------------------------\n";
1505   for my $sw (sort keys %db_switch_output_port) {
1506      if (exists $db_switch_parent{$sw}) {
1507         printf "%-25s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1508         }
1509      else {
1510         printf "%-25s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1511         }
1512      }
1513   print "\n";
1514
1515   print "Switch parent and children port inter-connection\n";
1516   print "------------------------------------------------\n";
[63]1517   for my $swport (sort keys %db_switch_connected_on_port) {
1518      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[4]1519      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1520         if (exists $db_switch_output_port{$sw}) {
1521            printf "%-25s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1522            }
1523         else {
1524            printf "%-25s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1525            }
1526         }
1527      }
[63]1528   return;
[4]1529   }
1530
1531sub cmd_exportsw_dot {
1532
[44]1533   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
[63]1534
[2]1535   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1536   my %db_switch_parent            = %{$switch_connection->{parent}};
1537   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1538   my %db_switch_link_with         = %{$switch_connection->{link_with}};
[25]1539   my %db_switch_global            = %{$switch_connection->{switch_db}};
1540
[2]1541   my %db_building= ();
[11]1542   for my $sw (@SWITCH) {
[63]1543      my ($building, $location) = split m/ \/ /xms, $sw->{location}, 2;
[2]1544      $db_building{$building} ||= {};
1545      $db_building{$building}->{$location} ||= {};
1546      $db_building{$building}->{$location}{ $sw->{hostname} } = 'y';
1547      }
[63]1548
1549
[2]1550   print "digraph G {\n";
1551
[4]1552   print "site [label = \"site\", color = black, fillcolor = gold, shape = invhouse, style = filled];\n";
1553   print "internet [label = \"internet\", color = black, fillcolor = cyan, shape = house, style = filled];\n";
[2]1554
1555   my $b=0;
1556   for my $building (keys %db_building) {
1557      $b++;
[63]1558
[4]1559      print "\"building$b\" [label = \"$building\", color = black, fillcolor = gold, style = filled];\n";
1560      print "site -> \"building$b\" [len = 2, color = firebrick];\n";
[2]1561
1562      my $l = 0;
1563      for my $loc (keys %{$db_building{$building}}) {
1564         $l++;
[63]1565
1566         print "\"location$b-$l\" [label = \"$building" . q{/} . join(q{\n}, split(m{ / }xms, $loc)) . "\", color = black, fillcolor = orange, style = filled];\n";
[33]1567#         print "\"location$b-$l\" [label = \"$building / $loc\", color = black, fillcolor = orange, style = filled];\n";
[4]1568         print "\"building$b\" -> \"location$b-$l\" [len = 2, color = firebrick]\n";
[2]1569
1570         for my $sw (keys %{$db_building{$building}->{$loc}}) {
1571
1572            print "\"$sw:$db_switch_output_port{$sw}\" [label = $db_switch_output_port{$sw}, color = black, fillcolor = lightblue,  peripheries = 2, style = filled];\n";
1573
[25]1574            my $swname  = $sw;
[63]1575               $swname .= q{\n-\n} . "$db_switch_global{$sw}->{model}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{model};
[25]1576            print "\"$sw\" [label = \"$swname\", color = black, fillcolor = palegreen, shape = rect, style = filled];\n";
[4]1577            print "\"location$b-$l\" -> \"$sw\" [len = 2, color = firebrick, arrowtail = dot]\n";
1578            print "\"$sw\" -> \"$sw:$db_switch_output_port{$sw}\" [len=2, style=bold, arrowhead = normal, arrowtail = invdot]\n";
[2]1579
1580
1581            for my $swport (keys %db_switch_connected_on_port) {
[63]1582               my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]1583               next if not $sw_connect eq $sw;
1584               next if $port_connect eq $db_switch_output_port{$sw};
1585               print "\"$sw:$port_connect\" [label = $port_connect, color = black, fillcolor = plum,  peripheries = 1, style = filled];\n";
[4]1586               print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, arrowhead= normal, arrowtail = inv]\n";
[2]1587              }
1588            }
1589         }
1590      }
1591
[63]1592#   print "Switch output port and parent port connection\n";
[2]1593#   print "---------------------------------------------\n";
1594   for my $sw (sort keys %db_switch_output_port) {
1595      if (exists $db_switch_parent{$sw}) {
1596#         printf "   \"%s:%s\" -> \"%s:%s\"\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{switch}, $db_switch_parent{$sw}->{port};
1597         }
1598      else {
1599         printf "   \"%s:%s\" -> internet\n", $sw, $db_switch_output_port{$sw};
1600         }
1601      }
1602   print "\n";
1603
1604#   print "Switch parent and children port inter-connection\n";
1605#   print "------------------------------------------------\n";
[63]1606   for my $swport (sort keys %db_switch_connected_on_port) {
1607      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]1608      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1609         if (exists $db_switch_output_port{$sw}) {
1610            printf "   \"%s:%s\" -> \"%s:%s\" [color = navyblue]\n", $sw, $db_switch_output_port{$sw}, $sw_connect, $port_connect;
1611            }
1612         else {
1613            printf "   \"%s\"   -> \"%s%s\"\n", $sw, $sw_connect, $port_connect;
1614            }
1615         }
1616      }
1617
1618print "}\n";
[63]1619   return;
[2]1620   }
1621
1622
1623__END__
1624
1625=head1 NAME
1626
1627klask - ports manager and finder for switch
1628
1629
[63]1630=head1 USAGE
[2]1631
1632 klask updatedb
[45]1633 klask exportdb --format [txt|html]
[2]1634
[5]1635 klask updatesw
1636 klask exportsw --format [txt|dot]
1637
[2]1638 klask searchdb computer
1639 klask search   computer
[69]1640 klask search-mac-on-switch switch mac_addr
[2]1641
[69]1642 klask ip-free --day number_of_day [vlan_name]
1643
[2]1644 klask enable  switch port
1645 klask disable swith port
1646 klask status  swith port
1647
1648
1649=head1 DESCRIPTION
1650
[6]1651klask is a small tool to find where is a host in a big network. klask mean search in brittany.
[2]1652
[6]1653Klask has now a web site dedicated for it !
[2]1654
[6]1655 http://servforge.legi.inpg.fr/projects/klask
1656
1657
[2]1658=head1 COMMANDS
1659
1660
1661=head2 search
1662
1663This 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.
1664
1665
1666=head2 enable
1667
1668This command activate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1669
1670
1671=head2 disable
1672
1673This command deactivate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1674
1675
1676=head2 status
1677
1678This 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.
1679
1680
1681=head2 updatedb
1682
1683This 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.
1684
1685
1686=head2 exportdb
1687
1688This 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...
1689
1690
[5]1691=head2 updatesw
1692
1693This 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.
1694
1695
1696=head2 exportsw --format [txt|dot]
1697
1698This 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.
1699
1700 klask exportsw --format dot > /tmp/map.dot
1701 dot -Tpng /tmp/map.dot > /tmp/map.png
1702
1703
1704
[2]1705=head1 CONFIGURATION
1706
1707Because 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 !
1708
1709Here an example, be aware with indent, it's important in YAML, do not use tabulation !
1710
1711 default:
1712   community: public
1713   snmpport: 161
1714
1715 network:
[5]1716   labnet:
1717     ip-subnet:
1718       - add: 192.168.1.0/24
1719       - add: 192.168.2.0/24
1720     interface: eth0
1721     main-router: gw1.labnet.local
[2]1722
[5]1723   schoolnet:
1724     ip-subnet:
1725       - add: 192.168.6.0/24
1726       - add: 192.168.7.0/24
1727     interface: eth0.38
1728     main-router: gw2.schoolnet.local
1729
[2]1730 switch:
1731   - hostname: sw1.klask.local
1732     portignore:
1733       - 1
1734       - 2
1735
1736   - hostname: sw2.klask.local
[5]1737     location: BatK / 2 / K203
1738     type: HP2424
[2]1739     portignore:
1740       - 1
1741       - 2
1742
1743I 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.
1744
1745
1746=head1 FILES
1747
1748 /etc/klask.conf
1749 /var/cache/klask/klaskdb
[7]1750 /var/cache/klask/switchdb
[2]1751
1752=head1 SEE ALSO
1753
1754Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
1755
1756
1757=head1 VERSION
1758
[36]1759$Id: klask 69 2010-11-02 15:43:26Z g7moreau $
[2]1760
1761
1762=head1 AUTHOR
1763
[5]1764Written by Gabriel Moreau, Grenoble - France
[2]1765
1766
[63]1767=head1 LICENSE AND COPYRIGHT
[2]1768
1769GPL version 2 or later and Perl equivalent
[45]1770
[63]1771Copyright (C) 2005-2009 Gabriel Moreau.
Note: See TracBrowser for help on using the repository browser.