END_HTML
return;
}
#---------------------------------------------------------------
sub cmd_bad_vlan_id {
@ARGV = @_;
my $days_before_alert = $DEFAULT{'days-before-alert'} || 15;
my $verbose;
GetOptions(
'day|d=i' => \$days_before_alert,
);
test_maindb_environnement();
my $computerdb = computerdb_load();
# create a database with the most recent computer by switch port
my %switchportdb = ();
LOOP_ON_IP_ADDRESS:
for my $ip (keys %{$computerdb}) {
# to be improve in the future
next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
next LOOP_ON_IP_ADDRESS if ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}) eq 'unknow';
next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{switch_port} eq '0';
my $ip_timestamp = $computerdb->{$ip}{timestamp};
my $ip_mac = $computerdb->{$ip}{mac_address};
my $ip_hostname_fq = $computerdb->{$ip}{hostname_fq};
my $swpt = sprintf "%-28s %2s",
$computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
$computerdb->{$ip}{switch_port_hr};
$switchportdb{$swpt} ||= {
ip => $ip,
timestamp => $ip_timestamp,
vlan => $computerdb->{$ip}{network},
hostname_fq => $ip_hostname_fq,
mac_address => $ip_mac,
};
# if float computer, set date 15 day before warning...
my $ip_timestamp_mod = $ip_timestamp;
my $ip_timestamp_ref = $switchportdb{$swpt}->{timestamp};
$ip_timestamp_mod -= $days_before_alert * 24 * 3600 if $ip_hostname_fq =~ m/$RE_FLOAT_HOSTNAME/;
$ip_timestamp_ref -= $days_before_alert * 24 * 3600 if $switchportdb{$swpt}->{hostname_fq} =~ m/$RE_FLOAT_HOSTNAME/;
if ($ip_timestamp_mod > $ip_timestamp_ref) {
$switchportdb{$swpt} = {
ip => $ip,
timestamp => $ip_timestamp,
vlan => $computerdb->{$ip}{network},
hostname_fq => $ip_hostname_fq,
mac_address => $ip_mac,
};
}
}
LOOP_ON_RECENT_COMPUTER:
for my $swpt (keys %switchportdb) {
next LOOP_ON_RECENT_COMPUTER if $swpt =~ m/^\s*0$/;
next LOOP_ON_RECENT_COMPUTER if $switchportdb{$swpt}->{hostname_fq} !~ m/$RE_FLOAT_HOSTNAME/;
my $src_ip = $switchportdb{$swpt}->{ip};
my $src_timestamp = 0;
LOOP_ON_IP_ADDRESS:
for my $ip (keys %{$computerdb}) {
next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{mac_address} ne $switchportdb{$swpt}->{mac_address};
next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} =~ m/$RE_FLOAT_HOSTNAME/;
next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{timestamp} < $src_timestamp;
$src_ip = $ip;
$src_timestamp = $computerdb->{$ip}{timestamp};
}
# keep only if float computer is the most recent
next LOOP_ON_RECENT_COMPUTER if $src_timestamp == 0;
next LOOP_ON_RECENT_COMPUTER if $switchportdb{$swpt}->{timestamp} < $src_timestamp;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $switchportdb{$swpt}->{timestamp};
$year += 1900;
$mon++;
my $date = sprintf '%04i-%02i-%02i/%02i:%02i', $year, $mon, $mday, $hour, $min;
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$src_ip}{timestamp};
$year += 1900;
$mon++;
my $src_date = sprintf '%04i-%02i-%02i/%02i:%02i', $year, $mon, $mday, $hour, $min;
my $vlan_id = get_current_vlan_id($computerdb->{$src_ip}{network});
printf "%s / %-10s +-> %-10s(%i) %s %s %s %s\n",
$swpt, $switchportdb{$swpt}->{vlan}, $computerdb->{$src_ip}{network}, $vlan_id,
$date,
$src_date,
$computerdb->{$src_ip}{mac_address},
$computerdb->{$src_ip}{hostname_fq};
}
}
#---------------------------------------------------------------
sub cmd_poe_enable {
@ARGV = @_;
my $verbose;
GetOptions(
'verbose|v' => \$verbose,
);
my $switch_name = shift @ARGV || q{};
my $switch_port = shift @ARGV || q{};
if ($switch_name eq q{} or $switch_port eq q{}) {
die "Usage: klask poe-enable SWITCH_NAME PORT\n";
}
for my $sw_name (split /,/, $switch_name) {
if (not defined $SWITCH_DB{$sw_name}) {
die "Switch $sw_name must be defined in klask configuration file\n";
}
my $search = $OID_NUMBER{'NApoeState'} . ".$switch_port"; # Only NEXANS Switch and low port number
my $sw = $SWITCH_DB{$sw_name};
my ($session, $error) = Net::SNMP->session(snmp_get_rwsession($sw));
print "$error \n" if $error;
my $result = $session->set_request(
-varbindlist => [$search, INTEGER, 8], # Only NEXANS
);
print $session->error()."\n" if $session->error_status();
$session->close;
}
cmd_poe_status($switch_name, $switch_port);
return;
}
#---------------------------------------------------------------
sub cmd_poe_disable {
@ARGV = @_;
my $verbose;
GetOptions(
'verbose|v' => \$verbose,
);
my $switch_name = shift @ARGV || q{};
my $switch_port = shift @ARGV || q{};
if ($switch_name eq q{} or $switch_port eq q{}) {
die "Usage: klask poe-disable SWITCH_NAME PORT\n";
}
for my $sw_name (split /,/, $switch_name) {
if (not defined $SWITCH_DB{$sw_name}) {
die "Switch $sw_name must be defined in klask configuration file\n";
}
my $search = $OID_NUMBER{'NApoeState'} . ".$switch_port"; # Only NEXANS Switch and low port number
my $sw = $SWITCH_DB{$sw_name};
my ($session, $error) = Net::SNMP->session(snmp_get_rwsession($sw));
print "$error \n" if $error;
my $result = $session->set_request(
-varbindlist => [$search, INTEGER, 2], # Only NEXANS
);
print $session->error()."\n" if $session->error_status();
$session->close;
}
cmd_poe_status($switch_name, $switch_port);
return;
}
#---------------------------------------------------------------
sub cmd_poe_status {
@ARGV = @_;
my $verbose;
GetOptions(
'verbose|v' => \$verbose,
);
my $switch_name = shift @ARGV || q{};
my $switch_port = shift @ARGV || q{};
if ($switch_name eq q{} or $switch_port eq q{}) {
die "Usage: klask poe-status SWITCH_NAME PORT\n";
}
for my $sw_name (split /,/, $switch_name) {
if (not defined $SWITCH_DB{$sw_name}) {
die "Switch $sw_name must be defined in klask configuration file\n";
}
my $search = $OID_NUMBER{'NApoeState'} . ".$switch_port"; # Only NEXANS Switch and low port number
my $sw = $SWITCH_DB{$sw_name};
my ($session, $error) = Net::SNMP->session( %{$sw->{'snmp_param_session'}} );
print "$error \n" if $error;
my $result = $session->get_request(
-varbindlist => [$search],
);
if (defined $result and $result->{$search} ne 'noSuchInstance') {
my $poe = $result->{$search} || 'empty';
$poe =~ s/8/enable/;
$poe =~ s/2/disable/;
printf "%s %s poe %s\n", $sw_name, $switch_port, $poe;
}
else {
print "Klask do not find PoE Status on switch $sw_name on port $switch_port\n";
}
$session->close;
}
return;
}
#---------------------------------------------------------------
# not finish - do not use
sub cmd_port_setvlan {
my $switch_name = shift || q{};
my $mac_address = shift || q{};
if ($switch_name eq q{} or $mac_address eq q{}) {
die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
}
$switch_name = join(',', map {$_->{hostname}} @SWITCH_LIST ) if $switch_name eq q{*};
for my $sw_name (split /,/, $switch_name) {
if (not defined $SWITCH_DB{$sw_name}) {
die "Switch $sw_name must be defined in klask configuration file\n";
}
my $research1 = $OID_NUMBER{'searchPort1'} . mac_address_hex2dec($mac_address);
my $research2 = $OID_NUMBER{'searchPort2'} .'.'. 0 . mac_address_hex2dec($mac_address);
print "Klask search OID $research1 on switch $sw_name\n";
print "Klask search OID $research2 on switch $sw_name\n";
my $sw = $SWITCH_DB{$sw_name};
my ($session, $error) = Net::SNMP->session( %{$sw->{'snmp_param_session'}} );
print "$error \n" if $error;
my $result = $session->get_request(
-varbindlist => [$research1]
);
if (not defined $result) {
$result = $session->get_request(
-varbindlist => [$research2]
);
$result->{$research1} = $result->{$research2} if defined $result;
}
if (defined $result and $result->{$research1} ne 'noSuchInstance') {
my $swport = $result->{$research1};
print "Klask find MAC $mac_address on switch $sw_name port $swport\n";
}
else {
print "Klask do not find MAC $mac_address on switch $sw_name\n";
}
$session->close;
}
return;
}
#---------------------------------------------------------------
sub cmd_port_getvlan {
@ARGV = @_;
my $verbose;
GetOptions(
'verbose|v' => \$verbose,
);
my $switch_name = shift @ARGV || q{};
my $switch_port = shift @ARGV || q{};
if ($switch_name eq q{} or $switch_port eq q{}) {
die "Usage: klask port-getvlan SWITCH_NAME PORT\n";
}
for my $sw_name (split /,/, $switch_name) {
if (not defined $SWITCH_DB{$sw_name}) {
die "Switch $sw_name must be defined in klask configuration file\n";
}
my $search = $OID_NUMBER{'vlanPortDefault'} . ".$switch_port";
my $sw = $SWITCH_DB{$sw_name};
my ($session, $error) = Net::SNMP->session( %{$sw->{'snmp_param_session'}} );
print "$error \n" if $error;
my $result = $session->get_request(
-varbindlist => [$search],
);
if (defined $result and $result->{$search} ne 'noSuchInstance') {
my $vlan_id = $result->{$search} || 'empty';
print "Klask VLAN Id $vlan_id on switch $sw_name on port $switch_port\n";
}
else {
print "Klask do not find VLAN Id on switch $sw_name on port $switch_port\n";
}
$session->close;
}
return;
}
#---------------------------------------------------------------
sub cmd_vlan_setname {
}
#---------------------------------------------------------------
# snmpset -v 1 -c public sw1-batG0-legi.hmg.priv "$OID_NUMBER{'HPicfReset'}.0" i 2;
sub cmd_rebootsw {
@ARGV = @_;
my $verbose;
GetOptions(
'verbose|v' => \$verbose,
);
my $switch_name = shift @ARGV || q{};
if ($switch_name eq q{}) {
die "Usage: klask rebootsw SWITCH_NAME\n";
}
for my $sw_name (split /,/, $switch_name) {
if (not defined $SWITCH_DB{$sw_name}) {
die "Switch $sw_name must be defined in klask configuration file\n";
}
my $sw = $SWITCH_DB{$sw_name};
my ($session, $error) = Net::SNMP->session(snmp_get_rwsession($sw));
print "$error \n" if $error;
my $result = $session->set_request(
-varbindlist => ["$OID_NUMBER{'HPicfReset'}.0", INTEGER, 2],
);
$session->close;
}
return;
}
#---------------------------------------------------------------
sub cmd_vlan_getname {
my $switch_name = shift || q{};
my $vlan_id = shift || q{};
if ($switch_name eq q{} or $vlan_id eq q{}) {
die "Usage: klask vlan-getname SWITCH_NAME VLAN_ID\n";
}
$switch_name = join(',', map {$_->{hostname}} @SWITCH_LIST ) if $switch_name eq q{*};
for my $sw_name (split /,/, $switch_name) {
if (not defined $SWITCH_DB{$sw_name}) {
die "Switch $sw_name must be defined in klask configuration file\n";
}
my $search_vlan_name = $OID_NUMBER{vlanName} . ".$vlan_id";
my $sw = $SWITCH_DB{$sw_name};
my ($session, $error) = Net::SNMP->session( %{$sw->{'snmp_param_session'}} );
print "$error \n" if $error;
my $result = $session->get_request(
-varbindlist => [$search_vlan_name]
);
if (defined $result and $result->{$search_vlan_name} ne 'noSuchInstance') {
my $vlan_name = $result->{$search_vlan_name} || 'empty';
print "Klask find VLAN $vlan_id on switch $sw_name with name $vlan_name\n";
}
else {
print "Klask do not find VLAN $vlan_id on switch $sw_name\n";
}
$session->close;
}
return;
}
#---------------------------------------------------------------
sub cmd_vlan_list {
my $switch_name = shift || q{};
if ($switch_name eq q{}) {
die "Usage: klask vlan-list SWITCH_NAME\n";
}
$switch_name = join(',', map {$_->{hostname}} @SWITCH_LIST ) if $switch_name eq q{*};
for my $sw_name (split /,/, $switch_name) {
if (not defined $SWITCH_DB{$sw_name}) {
die "Switch $sw_name must be defined in klask configuration file\n";
}
my $sw = $SWITCH_DB{$sw_name};
my ($session, $error) = Net::SNMP->session( %{$sw->{'snmp_param_session'}} );
print "$error \n" if $error;
my %vlandb = snmp_get_vlan_list($session);
$session->close;
print "VLAN_ID - VLAN_NAME # $sw_name\n";
for my $vlanid (keys %vlandb) {
printf "%7i - %s\n", $vlanid, $vlandb{$vlanid};
}
}
return;
}
#---------------------------------------------------------------
sub cmd_ip_location {
my $computerdb = computerdb_load();
LOOP_ON_IP_ADDRESS:
for my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
my $sw_hostname = $computerdb->{$ip}{switch_hostname} || q{};
next LOOP_ON_IP_ADDRESS if $sw_hostname eq 'unknow';
my $sw_location = q{};
LOOP_ON_ALL_SWITCH:
for my $sw (@SWITCH_LIST) {
next LOOP_ON_ALL_SWITCH if $sw_hostname ne $sw->{hostname};
$sw_location = $sw->{location};
last;
}
printf "%s: \"%s\"\n", $ip, $sw_location if not $sw_location eq q{};
}
return;
}
#---------------------------------------------------------------
sub cmd_ip_free {
@ARGV = @_;
my $days_to_death = $DEFAULT{'days-to-death'} || 365 * 2;
my $format = 'txt';
my $verbose;
GetOptions(
'day|d=i' => \$days_to_death,
'format|f=s' => \$format,
'verbose|v' => \$verbose,
);
my %possible_format = (
txt => \&cmd_ip_free_txt,
html => \&cmd_ip_free_html,
none => sub {},
);
$format = 'txt' if not defined $possible_format{$format};
my @vlan_name = @ARGV;
@vlan_name = get_list_network() if not @vlan_name;
my $computerdb = {};
$computerdb = computerdb_load() if -e "$KLASK_DB_FILE";
my $timestamp = time;
my $timestamp_barrier = $timestamp - (3600 * 24 * $days_to_death);
my %result_ip = ();
ALL_NETWORK:
for my $vlan (@vlan_name) {
my @ip_list = get_list_ip($vlan);
LOOP_ON_IP_ADDRESS:
for my $ip (@ip_list) {
if (exists $computerdb->{$ip}) {
next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{timestamp} > $timestamp_barrier;
my $mac_address = $computerdb->{$ip}{mac_address};
LOOP_ON_DATABASE:
for my $ip_db (keys %{$computerdb}) {
next LOOP_ON_DATABASE if $computerdb->{$ip_db}{mac_address} ne $mac_address;
next LOOP_ON_IP_ADDRESS if $computerdb->{$ip_db}{timestamp} > $timestamp_barrier;
}
}
my $ip_date_last_detection = '';
if (exists $computerdb->{$ip}) {
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
$year += 1900;
$mon++;
$ip_date_last_detection = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
}
my $packed_ip = scalar gethostbyname($ip);
my $hostname_fq = 'unknown';
$hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) || 'unknown' if defined $packed_ip and get_current_scan_mode($vlan) eq 'active';
next LOOP_ON_IP_ADDRESS if $hostname_fq =~ m/$RE_FLOAT_HOSTNAME/;
$result_ip{$ip} ||= {};
$result_ip{$ip}->{date_last_detection} = $ip_date_last_detection;
$result_ip{$ip}->{hostname_fq} = $hostname_fq;
$result_ip{$ip}->{vlan} = $vlan;
printf "VERBOSE_1: %-15s %-12s %s\n", $ip, $vlan, $hostname_fq if $verbose;
}
}
$possible_format{$format}->(%result_ip);
}
#---------------------------------------------------------------
sub cmd_ip_free_txt {
my %result_ip = @_;
printf "%-15s %-40s %-16s %s\n", qw(IPv4-Address Hostname-FQ Date VLAN);
print "-------------------------------------------------------------------------------\n";
LOOP_ON_IP_ADDRESS:
for my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
my $vlan_nameid = $result_ip{$ip}->{vlan}.'('.get_current_vlan_id($result_ip{$ip}->{vlan}).')';
printf "%-15s %-40s %-16s %s\n", $ip, $result_ip{$ip}->{hostname_fq}, $result_ip{$ip}->{date_last_detection}, $vlan_nameid;
}
}
#---------------------------------------------------------------
sub cmd_ip_free_html {
my %result_ip = @_;
print <<'END_HTML';
Klask Free IP Database
IPv4-Address
Hostname-FQ
VLAN
Date
IPv4-Address
Hostname-FQ
VLAN
Date
END_HTML
my $typerow = 'even';
LOOP_ON_IP_ADDRESS:
for my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
$typerow = $typerow eq 'even' ? 'odd' : 'even';
my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
my ( $host_short ) = split m/ \. /xms, $result_ip{$ip}->{hostname_fq};
my $vlan_nameid = $result_ip{$ip}->{vlan}.'('.get_current_vlan_id($result_ip{$ip}->{vlan}).')';
print <<"END_HTML";
$ip
$result_ip{$ip}->{hostname_fq}
$vlan_nameid
$result_ip{$ip}->{date_last_detection}
END_HTML
}
print <<'END_HTML';
END_HTML
}
#---------------------------------------------------------------
sub cmd_enable {
@ARGV = @_;
my $verbose;
GetOptions(
'verbose|v' => \$verbose,
);
my $switch_name = shift @ARGV || q{};
my $port = shift @ARGV || q{};
if ($switch_name eq q{} or $port eq q{}) {
die "Usage: klask disable SWITCH_NAME PORT\n";
}
if (not defined $SWITCH_DB{$switch_name}) {
die "Switch $switch_name must be defined in klask configuration file\n";
}
my $sw = $SWITCH_DB{$switch_name};
my ($session, $error) = Net::SNMP->session(snmp_get_rwsession($sw));
print "$error \n" if $error;
# Retrieve numeric port value
my $port_num = snmp_get_switchport_hr2num($session, normalize_port_human_readable($port), $verbose ? 'yes' : '');
die "Error : Port $port does not exist on switch $switch_name\n" if not $port_num =~ m/^\d+$/;
my $search_portstatus = $OID_NUMBER{'portUpDown'} .'.'. $port_num;
print "Info: switch $switch_name port $port SNMP OID $search_portstatus\n" if $verbose;
my $result = $session->set_request(
-varbindlist => [$search_portstatus, INTEGER, 1],
);
print $session->error()."\n" if $session->error_status();
$session->close;
#snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
#snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
#system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
return;
}
#---------------------------------------------------------------
sub cmd_disable {
@ARGV = @_;
my $verbose;
GetOptions(
'verbose|v' => \$verbose,
);
my $switch_name = shift @ARGV || q{};
my $port = shift @ARGV || q{};
if ($switch_name eq q{} or $port eq q{}) {
die "Usage: klask disable SWITCH_NAME PORT\n";
}
if (not defined $SWITCH_DB{$switch_name}) {
die "Switch $switch_name must be defined in klask configuration file\n";
}
my $sw = $SWITCH_DB{$switch_name};
my ($session, $error) = Net::SNMP->session(snmp_get_rwsession($sw));
print "$error \n" if $error;
# Retrieve numeric port value
my $port_num = snmp_get_switchport_hr2num($session, normalize_port_human_readable($port), $verbose ? 'yes' : '');
die "Error : Port $port does not exist on switch $switch_name\n" if not $port_num =~ m/^\d+$/;
my $search_portstatus = $OID_NUMBER{'portUpDown'} .'.'. $port_num;
print "Info: switch $switch_name port $port SNMP OID $search_portstatus\n" if $verbose;
my $result = $session->set_request(
-varbindlist => [$search_portstatus, INTEGER, 2],
);
print $session->error()."\n" if $session->error_status();
$session->close;
#system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
return;
}
#---------------------------------------------------------------
sub cmd_status {
@ARGV = @_;
my $verbose;
GetOptions(
'verbose|v' => \$verbose,
);
my $switch_name = shift @ARGV || q{};
my $port = shift @ARGV || q{};
if ($switch_name eq q{} or $port eq q{}) {
die "Usage: klask status SWITCH_NAME PORT\n";
}
if (not defined $SWITCH_DB{$switch_name}) {
die "Switch $switch_name must be defined in klask configuration file\n";
}
my $sw = $SWITCH_DB{$switch_name};
my ($session, $error) = Net::SNMP->session( %{$sw->{'snmp_param_session'}} );
print "$error \n" if $error;
# Retrieve numeric port value
my $port_num = snmp_get_switchport_hr2num($session, normalize_port_human_readable($port), $verbose ? 'yes' : '');
die "Error : Port $port does not exist on switch $switch_name\n" if not $port_num =~ m/^\d+$/;
my $search_portstatus = $OID_NUMBER{'portUpDown'} .'.'. $port_num;
print "Info: switch $switch_name port $port ($port_num) SNMP OID $search_portstatus\n" if $verbose;
my $result = $session->get_request(
-varbindlist => [$search_portstatus]
);
print $session->error()."\n" if $session->error_status();
if (defined $result) {
print "$PORT_UPDOWN{$result->{$search_portstatus}}\n";
}
$session->close;
#system "snmpget -v 1 -c public $switch_name 1.3.6.1.2.1.2.2.1.7.$port";
return;
}
#---------------------------------------------------------------
sub cmd_search_mac_on_switch {
@ARGV = @_;
my $verbose;
my $vlan_id = 0;
GetOptions(
'verbose|v' => \$verbose,
'vlan|l=i' => \$vlan_id,
);
my $switch_name = shift @ARGV || q{};
my $mac_address = shift @ARGV || q{};
if ($switch_name eq q{} or $mac_address eq q{}) {
die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
}
$mac_address = normalize_mac_address($mac_address);
$switch_name = join(',', map {$_->{hostname}} @SWITCH_LIST ) if $switch_name eq q{*} or $switch_name eq q{all};
for my $sw_name (split /,/, $switch_name) {
if (not defined $SWITCH_DB{$sw_name}) {
die "Switch $sw_name must be defined in klask configuration file\n";
}
my $research1 = $OID_NUMBER{'searchPort1'} . mac_address_hex2dec($mac_address);
my $research2 = $OID_NUMBER{'searchPort2'} .'.'. $vlan_id . mac_address_hex2dec($mac_address);
print "Klask search OID $research1 on switch $sw_name\n" if $verbose;
print "Klask search OID $research2 on switch $sw_name\n" if $verbose;
my $sw = $SWITCH_DB{$sw_name};
my ($session, $error) = Net::SNMP->session( %{$sw->{'snmp_param_session'}} );
print "$error \n" if $error;
my $result = $session->get_request(
-varbindlist => [$research1]
);
if (not defined $result) {
$result = $session->get_request(
-varbindlist => [$research2]
);
$result->{$research1} = $result->{$research2} if defined $result;
}
if (defined $result and $result->{$research1} ne 'noSuchInstance') {
my $swport_num = $result->{$research1};
my $swport_hr = snmp_get_switchport_num2hr($session, $swport_num);
print "Klask find MAC $mac_address on switch $sw_name port $swport_hr\n";
}
else {
print "Klask do not find MAC $mac_address on switch $sw_name\n" if $verbose;
}
$session->close;
}
return;
}
#---------------------------------------------------------------
sub cmd_updatesw {
@ARGV = @_;
my $verbose;
GetOptions(
'verbose|v' => \$verbose,
);
init_switch_names('yes'); #nomme les switchs
print "\n";
my %where = ();
my %db_switch_output_port = ();
my %db_switch_ip_hostnamefq = ();
DETECT_ALL_ROUTER:
for my $one_router ( get_list_main_router(get_list_network()) ) {
print "Info: router loop $one_router\n" if $verbose;
my %resol_arp = resolve_ip_arp_host($one_router, q{*}, q{low}); # resolution arp
next DETECT_ALL_ROUTER if $resol_arp{mac_address} eq 'unknow';
print "VERBOSE_1: Router detected $resol_arp{ipv4_address} - $resol_arp{mac_address}\n" if $verbose;
my $vlan_name = get_current_vlan_name_for_interface($resol_arp{interface});
my $vlan_id = get_current_vlan_id($vlan_name);
$where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address},$vlan_id); # retrouve les emplacements des routeurs
}
ALL_ROUTER_IP_ADDRESS:
for my $ip_router (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip_router}; # /a priori/ idiot car ne sers à rien...
ALL_SWITCH_CONNECTED:
for my $switch_detected ( keys %{$where{$ip_router}} ) {
my $switch = $where{$ip_router}->{$switch_detected};
next ALL_SWITCH_CONNECTED if $switch->{port} eq '0';
$db_switch_output_port{$switch->{hostname}} = $switch->{port_hr};
print "VERBOSE_2: output port $switch->{hostname} : $switch->{port_hr}\n" if $verbose;
}
}
my %db_switch_link_with = ();
my @list_all_switch = ();
my @list_switch_ipv4 = ();
for my $sw (@SWITCH_LIST) {
push @list_all_switch, $sw->{hostname};
}
my $timestamp = time;
ALL_SWITCH:
for my $one_switch (@list_all_switch) {
my %resol_arp = resolve_ip_arp_host($one_switch, q{*}, q{low}); # arp resolution
if (exists $SWITCH_DB{$one_switch}{'fake-ip'}) {
print "WARNING: fake ip on switch $one_switch -> $SWITCH_DB{$one_switch}{'fake-ip'} / $resol_arp{ipv4_address}\n" if $verbose;
my %resol_arp_alt = resolve_ip_arp_host($SWITCH_DB{$one_switch}{'fake-ip'}, q{*}, q{low}); # arp resolution
if ($resol_arp_alt{mac_address} ne 'unknow') {
$resol_arp{mac_address} = $resol_arp_alt{mac_address};
$resol_arp{interface} = $resol_arp_alt{interface};
$resol_arp{ipv4_address} .= '*';
}
}
print "Info: switch loop $one_switch\n" if $verbose;
next ALL_SWITCH if $resol_arp{mac_address} eq 'unknow';
push @list_switch_ipv4, $resol_arp{ipv4_address};
my $vlan_name = get_current_vlan_name_for_interface($resol_arp{interface});
my $vlan_id = get_current_vlan_id($vlan_name);
$where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address},$vlan_id); # find port on all switch
if ($verbose) {
print "VERBOSE_3: $one_switch $resol_arp{ipv4_address} $resol_arp{mac_address}\n";
print "VERBOSE_3: $one_switch --- ",
join(' + ', keys %{$where{$resol_arp{ipv4_address}}}),
"\n";
}
$db_switch_ip_hostnamefq{$resol_arp{ipv4_address}} = $resol_arp{hostname_fq};
print "VERBOSE_4: db_switch_ip_hostnamefq $resol_arp{ipv4_address} -> $resol_arp{hostname_fq}\n" if $verbose;
$SWITCH_DB{$one_switch}->{ipv4_address} = $resol_arp{ipv4_address};
$SWITCH_DB{$one_switch}->{mac_address} = $resol_arp{mac_address};
$SWITCH_DB{$one_switch}->{timestamp} = $timestamp;
}
ALL_SWITCH_IP_ADDRESS:
for my $ip (@list_switch_ipv4) {
# for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
print "VERBOSE_5: loop on $db_switch_ip_hostnamefq{$ip}\n" if $verbose;
next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
# next ALL_SWITCH_IP_ADDRESS if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostnamefq{$ip} };
DETECTED_SWITCH:
for my $switch_detected ( keys %{$where{$ip}} ) {
my $switch = $where{$ip}->{$switch_detected};
print "VERBOSE_6: $db_switch_ip_hostnamefq{$ip} -> $switch->{hostname} : $switch->{port_hr}\n" if $verbose;
next if $switch->{port} eq '0';
next if $switch->{port_hr} eq $db_switch_output_port{$switch->{hostname}};
next if $switch->{hostname} eq $db_switch_ip_hostnamefq{$ip}; # $computerdb->{$ip}{hostname};
$db_switch_link_with{ $db_switch_ip_hostnamefq{$ip} } ||= {};
$db_switch_link_with{ $db_switch_ip_hostnamefq{$ip} }->{ $switch->{hostname} } = $switch->{port_hr};
print "VERBOSE_7: +++++\n" if $verbose;
}
}
my %db_switch_connected_on_port = ();
my $maybe_more_than_one_switch_connected = 'yes';
my $cloop = 0;
while ($maybe_more_than_one_switch_connected eq 'yes' and $cloop < 100) {
$cloop++;
print "VERBOSE_9: cloop reduction step: $cloop\n" if $verbose;
for my $sw (keys %db_switch_link_with) {
for my $connect (keys %{$db_switch_link_with{$sw}}) {
my $port_hr = $db_switch_link_with{$sw}->{$connect};
$db_switch_connected_on_port{"$connect$SEP_SWITCH_PORT$port_hr"} ||= {};
$db_switch_connected_on_port{"$connect$SEP_SWITCH_PORT$port_hr"}->{$sw}++; # Just to define the key
}
}
$maybe_more_than_one_switch_connected = 'no';
SWITCH_AND_PORT:
for my $swport (keys %db_switch_connected_on_port) {
next if keys %{$db_switch_connected_on_port{$swport}} == 1;
$maybe_more_than_one_switch_connected = 'yes';
my ($sw_connect, $port_connect) = split m/ $SEP_SWITCH_PORT /xms, $swport, 2;
my @sw_on_same_port = keys %{$db_switch_connected_on_port{$swport}};
print "VERBOSE_10: $swport -- ".$#sw_on_same_port." -- @sw_on_same_port\n" if $verbose;
CONNECTED:
for my $sw_connected (@sw_on_same_port) {
next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
$db_switch_connected_on_port{$swport} = {$sw_connected => 1};
for my $other_sw (@sw_on_same_port) {
next if $other_sw eq $sw_connected;
delete $db_switch_link_with{$other_sw}->{$sw_connect};
}
# We can not do better for this switch for this loop
next SWITCH_AND_PORT;
}
}
}
my %db_switch_parent =();
for my $sw (keys %db_switch_link_with) {
for my $connect (keys %{$db_switch_link_with{$sw}}) {
my $port_hr = $db_switch_link_with{$sw}->{$connect};
$db_switch_connected_on_port{"$connect$SEP_SWITCH_PORT$port_hr"} ||= {};
$db_switch_connected_on_port{"$connect$SEP_SWITCH_PORT$port_hr"}->{$sw} = $port_hr;
$db_switch_parent{$sw} = {switch => $connect, port_hr => $port_hr};
}
}
print "Switch output port and parent port connection\n";
print "---------------------------------------------\n";
for my $sw (sort keys %db_switch_output_port) {
if (exists $db_switch_parent{$sw}) {
printf "%-28s %2s +--> %2s %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port_hr}, $db_switch_parent{$sw}->{'switch'};
}
else {
printf "%-28s %2s +--> router\n", $sw, $db_switch_output_port{$sw};
}
}
print "\n";
print "Switch parent and children port inter-connection\n";
print "------------------------------------------------\n";
for my $swport (sort keys %db_switch_connected_on_port) {
my ($sw_connect, $port_connect) = split m/ $SEP_SWITCH_PORT /xms, $swport, 2;
for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
if (exists $db_switch_output_port{$sw}) {
printf "%-28s %2s <--+ %2s %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
}
else {
printf "%-28s %2s <--+ %-25s\n", $sw_connect, $port_connect, $sw;
}
}
}
my $switch_connection = {
output_port => \%db_switch_output_port,
parent => \%db_switch_parent,
connected_on_port => \%db_switch_connected_on_port,
link_with => \%db_switch_link_with,
switch_db => \%SWITCH_DB,
};
YAML::Syck::DumpFile("$KLASK_SW_FILE", $switch_connection);
return;
}
#---------------------------------------------------------------
sub cmd_exportsw {
@ARGV = @_;
test_switchdb_environnement();
my $format = 'txt';
GetOptions(
'format|f=s' => \$format,
);
my %possible_format = (
txt => \&cmd_exportsw_txt,
dot => \&cmd_exportsw_dot,
);
$format = 'txt' if not defined $possible_format{$format};
$possible_format{$format}->(@ARGV);
return;
}
#---------------------------------------------------------------
sub cmd_exportsw_txt {
my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
my %db_switch_output_port = %{$switch_connection->{output_port}};
my %db_switch_parent = %{$switch_connection->{parent}};
my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
print "Switch output port and parent port connection\n";
print "---------------------------------------------\n";
for my $sw (sort keys %db_switch_output_port) {
my $arrow ='-->';
$arrow ='==>' if $db_switch_output_port{$sw} =~ m/^(Trk|Br|Po)/;
if (exists $db_switch_parent{$sw}) {
printf "%-28s %8s %3s %-8s %-25s\n", $sw, $db_switch_output_port{$sw}, $arrow, $db_switch_parent{$sw}->{port_hr}, $db_switch_parent{$sw}->{'switch'};
}
else {
printf "%-28s %8s %3s %-8s %-25s\n", $sw, $db_switch_output_port{$sw}, $arrow, '', 'router';
}
}
print "\n";
print "Switch parent and children port inter-connection\n";
print "------------------------------------------------\n";
for my $swport (sort keys %db_switch_connected_on_port) {
my ($sw_connect, $port_connect) = split m/ $SEP_SWITCH_PORT /xms, $swport, 2;
for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
my $arrow ='<--';
$arrow ='<==' if $port_connect =~ m/^(Trk|Br|Po)/;
if (exists $db_switch_output_port{$sw}) {
printf "%-28s %8s %3s %-8s %-25s\n", $sw_connect, $port_connect, $arrow, $db_switch_output_port{$sw}, $sw;
}
else {
printf "%-28s %8s %3s %-8s %-25s\n", $sw_connect, $port_connect, $arrow, '', $sw;
}
}
}
return;
}
#---------------------------------------------------------------
sub cmd_exportsw_dot {
my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
my %db_switch_output_port = %{$switch_connection->{output_port}};
my %db_switch_parent = %{$switch_connection->{parent}};
my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
my %db_switch_link_with = %{$switch_connection->{link_with}};
my %db_switch_global = %{$switch_connection->{switch_db}};
my %db_building= ();
for my $sw (@SWITCH_LIST) {
my ($building, $location) = split m/ \/ /xms, $sw->{location}, 2;
$db_building{$building} ||= {};
$db_building{$building}->{$location} ||= {};
$db_building{$building}->{$location}{ $sw->{hostname} } = 'y';
}
print "digraph G {\n";
print "rankdir = LR;\n";
#print "splines=polyline;\n";
print "site [label = \"site\", color = black, fillcolor = gold, shape = invhouse, style = filled];\n";
print "internet [label = \"internet\", color = black, fillcolor = cyan, shape = house, style = filled];\n";
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime time;
$year += 1900;
$mon++;
my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
print "\"$date\" [ color = white, fillcolor = black, shape = polygon, sides=14, style = filled, fontcolor = white ]\n";
print "site -> \"$date\" [color = white];\n";
my $b=0;
for my $building (keys %db_building) {
$b++;
print "\"building$b\" [label = \"$building\", color = black, fillcolor = gold, style = filled];\n";
print "site -> \"building$b\" [len = 2, color = firebrick];\n";
my $l = 0;
for my $loc (keys %{$db_building{$building}}) {
$l++;
print "\"location$b-$l\" [label = \"$building" . q{/} . join(q{\n}, split(m{ / }xms, $loc)) . "\", color = black, fillcolor = orange, style = filled];\n";
# print "\"location$b-$l\" [label = \"$building / $loc\", color = black, fillcolor = orange, style = filled];\n";
print "\"building$b\" -> \"location$b-$l\" [len = 2, color = firebrick]\n";
for my $sw (keys %{$db_building{$building}->{$loc}}) {
print "\"$sw:$db_switch_output_port{$sw}\" [label = \"".format_aggregator4dot($db_switch_output_port{$sw})"\", color = black, fillcolor = lightblue, peripheries = 2, style = filled];\n";
my $swname = $sw;
$swname .= q{\n-\n} . "$db_switch_global{$sw}->{model}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{model};
print "\"$sw\" [label = \"$swname\", color = black, fillcolor = palegreen, shape = rect, style = filled];\n";
print "\"location$b-$l\" -> \"$sw\" [len = 2, color = firebrick, arrowtail = dot]\n";
print "\"$sw\" -> \"$sw:$db_switch_output_port{$sw}\" [len=2, style=bold, arrowhead = normal, arrowtail = invdot]\n";
for my $swport (keys %db_switch_connected_on_port) {
my ($sw_connect, $port_connect) = split m/ $SEP_SWITCH_PORT /xms, $swport, 2;
next if not $sw_connect eq $sw;
next if $port_connect eq $db_switch_output_port{$sw};
print "\"$sw:$port_connect\" [label = \"".format_aggregator4dot($port_connect)."\", color = black, fillcolor = plum, peripheries = 1, style = filled];\n";
print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, arrowhead= normal, arrowtail = inv]\n";
}
}
}
}
# print "Switch output port and parent port connection\n";
# print "---------------------------------------------\n";
for my $sw (sort keys %db_switch_output_port) {
if (exists $db_switch_parent{$sw}) {
# printf " \"%s:%s\" -> \"%s:%s\"\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{'switch'}, $db_switch_parent{$sw}->{port};
}
else {
printf " \"%s:%s\" -> internet\n", $sw, $db_switch_output_port{$sw};
}
}
print "\n";
# print "Switch parent and children port inter-connection\n";
# print "------------------------------------------------\n";
for my $swport (sort keys %db_switch_connected_on_port) {
my ($sw_connect, $port_connect) = split m/ $SEP_SWITCH_PORT /xms, $swport, 2;
for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
if (exists $db_switch_output_port{$sw}) {
printf " \"%s:%s\" -> \"%s:%s\" [color = navyblue]\n", $sw, $db_switch_output_port{$sw}, $sw_connect, $port_connect;
}
else {
printf " \"%s\" -> \"%s%s\"\n", $sw, $sw_connect, $port_connect;
}
}
}
print "}\n";
return;
}
################################################################
# documentation
################################################################
__END__
=head1 NAME
klask - port and search manager for switches, map management
=head1 USAGE
klask version
klask help
klask updatedb [--verbose|-v] [--verb-description|-d] [--chk-hostname|-h] [--chk-location|-l]
klask exportdb [--format|-f txt|html]
klask removedb IP* computer*
klask cleandb [--verbose|-v] --day number_of_day --repair-dns
klask updatesw [--verbose|-v]
klask exportsw [--format|-f txt|dot]
klask searchdb [--kind|-k host|mac] computer [mac-address]
klask search computer
klask search-mac-on-switch [--verbose|-v] [--vlan|-i vlan-id] switch mac_addr
klask ip-free [--verbose|-v] [--day|-d days-to-death] [--format|-f txt|html] [vlan_name]
klask bad-vlan-id [--day|-d days_before_alert]
klask enable [--verbose|-v] switch port
klask disable [--verbose|-v] switch port
klask status [--verbose|-v] switch port
klask poe-enable [--verbose|-v] switch port
klask poe-disable [--verbose|-v] switch port
klask poe-status [--verbose|-v] switch port
klask vlan-getname switch vlan-id
klask vlan-list switch
=head1 DESCRIPTION
Klask is a small tool to find where is connected a host in a big network
and on which VLAN.
Klask mean search in brittany.
No hight level protocol like CDL, LLDP are use.
Everything is just done with SNMP request on MAC address.
Limitation : loop cannot be detected and could be problematic when the map is created (C method).
If you use PVST or MSTP and create loop between VLAN,
you have to use C functionality on switch port to cut manually loop
(see config file below).
When you use a management port to administrate a switch,
it's not possible to create the map with this switch because it does not have a MAC address,
so other switch cannot find the real downlink port...
One way to work around this problem is, if you have a computer directly connected on the switch,
to put this IP as the fake ip for the switch.
The MAC address associated will be use just for the map detection.
The C parameter is defined in the config file.
Klask has now a web site dedicated for it: L!
=head1 COMMANDS
Some command are defined in the source code but are not documented here.
Theses could be not well defined, not finished, not well tested...
You can read the source code and use them at your own risk
(like for all the Klask code).
=head2 search
klask search computer
This 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.
=head2 search-mac-on-switch
klask search-mac-on-switch [--verbose|-v] [--vlan|-i vlan-id] switch mac_addr
This command search a MAC address on a switch.
To search on all switch, you could put C<'*'> or C.
The VLAN parameter could help.
=head2 enable
klask enable [--verbose|-v] switch port
This command activate a port (or an agrregate bridge port) on a switch by SNMP.
So you need to give the switch name and a port on the command line.
See L.
Warning: You need to have the SNMP write access on the switch in order to modify it's configuration.
=head2 disable
klask disable [--verbose|-v] switch port
This command deactivate a port (or an agrregate bridge port) on a switch by SNMP.
So you need to give the switch name and a port on the command line.
See L.
Warning: You need to have the SNMP write access on the switch in order to modify it's configuration.
=head2 status
klask status [--verbose|-v] switch port
This command return the status of a port number on a switch by SNMP.
The return value could be C or C word.
So you need to give the switch name and a port on the command line.
See L.
If it's not possible to change port status with command L and L
(SNMP community read write access),
it's always possible to have the port status even for bridge agrregate port.
=head2 updatedb
klask updatedb [--verbose|-v] [--verb-description|-d] [--chk-hostname|-h] [--chk-location|-l]
This command will scan networks and update the computer database.
To know which are the cmputer scanned, you have to configure the file F.
This file is easy to read and write because Klask use YAML format and not XML
(see L).
Option are not stable and could be use manually when you have a new kind of switch.
Maybe some option will be transfered in a future C command!
The network parameter C can have two values: C or C.
By default, a network is C.
This means that an C command is done at the beginning on all the IP of the network
and the computers that was not detected in this pass, but where their Klask entry is less than one week,
will have an C
(some OS do not respond to C but a computer have to respond to C if it want to interact with other).
In the scan mode C, no C and no C are done.
It's good for big subnet with few computer (telephone...).
The idea of the C scan mode is to force computer to regulary send packet over the network.
=head2 exportdb
klask exportdb [--format|-f txt|html]
This command print the content of the computer database.
There is actually only two format : TXT and HTML.
By default, format is TXT.
It's very easy to have more format, it's just need times...
=head2 removedb
klask removedb IP* computer*
This command remove an entry in the database.
There is only one kind of parameter, the IP of the computers to be removed.
You can put as many IP as you want...
Computer DNS names are also a valid entry because a DNS resolver is executed at the beginning.
=head2 cleandb
klask cleandb [--verbose|-v] --day number_of_day --repair-dns
Remove double entry (same MAC-Address) in the computer database when the older one is older than X day (C<--day>) the new one.
Computer name beginning by 'float' (regex C<^float>) are not really taken into account but could be remove.
This could be configure with the global regex parameter C in the configuration file F.
This functionality could be use when computer define in VLAN 1
could have a float IP when they are connected on VLAN 2.
In the Klask database, the float DNS entries are less important.
When reverse DNS has not been done by the past, option C<--repair-dns> force a reverse DNS check on all unkown host.
=head2 updatesw
klask updatesw [--verbose|-v]
This command build a map of your manageable switch on your network.
The list of the switches must be given in the file F (see L).
=head2 exportsw
klask exportsw [--format|-f txt|dot]
This 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.
By default, format is TXT.
klask exportsw --format dot > /tmp/map.dot
dot -Tpng /tmp/map.dot > /tmp/map.png
=head2 ip-free
klask ip-free [--verbose|-v] [--day|-d days-to-death] [--format|-f txt|html] [vlan_name]
This command return IP address that was not use (detected by Klask) at this time.
The list returned could be limited to just one VLAN.
IP returned could have been never used or no computer have been detected since the number of days specified
(2 years by default).
This parameter could also be define in the configuration file F (see L).
default:
days-to-death: 730
Computer that does not have the good IP but takes a float one (see L) are taken into account.
=head2 bad-vlan-id
klask bad-vlan-id [--day|-d days_before_alert]
This command return a list of switch port that are not configure with the good VLAN.
Computer which are in bad VLAN are detected with the float regex parameter (see L)
and another prior trace where they had the good IP (good DNS name).
The computer must stay connected on a bad VLAN more than XX days (15 days by default) before alert.
This parameter could also define in the configuration file F (see L).
default:
days-before-alert: 15
This functionality is not need if your switch use RADIUS 802.1X configuration...
=head2 poe-enable
klask poe-enable [--verbose|-v] switch port
This command activate the PoE (Power over Ethernet) on a switch port by SNMP.
So you need to give the switch name and a port on the command line.
See L.
Warning: Only NEXANS switches are supported (we do not have other switch for testing).
You need to have the SNMP write access on the switch in order to modify it's configuration.
=head2 poe-disable
klask poe-disable [--verbose|-v] switch port
This command deactivate the PoE (Power over Ethernet) on a switch port by SNMP.
So you need to give the switch name and a port on the command line.
See L.
Warning: Only NEXANS switches are supported (we do not have other switch for testing).
You need to have the SNMP write access on the switch in order to modify it's configuration.
=head2 poe-status
klask poe-status [--verbose|-v] switch port
This command return the status of the PoE (Power over Ethernet) on a switch port by SNMP.
The return value could be C or C word.
So you need to give the switch name and a port on the command line.
See L.
If it's not possible to change the PoE status with command L and L
(SNMP community read write access),
it's always possible to have the PoE port status.
Warning: Only NEXANS switches are supported (we do not have other switch for testing).
=head1 CONFIGURATION
Because Klask need many parameters, it's not possible actually to use command line parameters for everything.
The configuration is done in a F YAML file.
This format have many advantage over XML, it's easier to read and to write !
Here an example, be aware with indent, it's important in YAML, do not use tabulation !
default:
community: public
community-rw: private
snmpport: 161
float-regex: '(?^msx: ^float )'
scan-mode: active
network:
labnet:
ip-subnet:
- add: 192.168.1.0/24
- add: 192.168.2.0/24
interface: eth0
vlan-id: 12
main-router: gw1.labnet.local
schoolnet:
ip-subnet:
- add: 192.168.3.0/24
- add: 192.168.4.0/24
interface: eth0.38
vlan-id: 13
main-router: gw2.schoolnet.local
scan-mode: passive
etunet:
ip-subnet:
- add: 192.168.5.0/24
interface: eth2
vlan-id: 14
main-router: gw3.etunet.local
scan-mode: passive
switch:
- hostname: sw1.klask.local
location: BatY / 1 floor / K004
portignore:
- 1
- 2
- hostname: sw2.klask.local
location: BatY / 2 floor / K203
type: HP2424
portignore:
- 1
- 2
fake-ip: 192.168.9.14
- hostname: sw3.klask.local
location: BatY / 2 floor / K203
I 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 an 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 was important if your switchs are cascades
(right now, method C find them automatically)
and is still important if you have loop (with PVST or MSTP).
Just put the ports numbers between switch.
The C parameter is use to get SNMP data on switch.
It could be overload for each switch.
By default, it's value is C and you have to configure a readonly word for safety reason.
Some few command change the switch state as the commands L and L.
In theses rares cases, you need a readwrite SNMP community word define in your configuration file.
Klask then use since version C<0.6.2> the C parameter which by default is egal to C.
=head1 ABBREVIATION FOR PORT
HP Procurve and Nexans switches have a simplistic numbering scheme.
It's just number: 1, 2, 3... 24.
On HP8000 chassis, ports names begin with an uppercase letter: A1, A2...
Nothing is done on theses ports names.
On HP Comware and DELL, port digitization schema use a port speed word (generally a very verbose word)
followed by tree number.
In order to have short name,
we made the following rules:
Bridge-Aggregation -> Br
FastEthernet -> Fa
Forty-GigabitEthernet -> Fo
FortyGigabitEthernet -> Fo
GigabitEthernet -> Gi
Giga -> Gi
Port-Channel -> Po
Ten-GigabitEthernet -> Te
TenGigabitEthernet -> Te
Ten -> Te
All Klask command automatically normalize the port name on standart output
and also on input command line.
In the case of use an aggregator port (Po, Tk, Br ...),
the real ports used are also return.
=head1 FILES
/etc/klask/klask.conf
/var/lib/klask/klaskdb
/var/lib/klask/switchdb
=head1 SEE ALSO
Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
=over
=item * L
=item * L
=back
=head1 VERSION
$Id: klask 250 2017-09-08 14:53:06Z g7moreau $
=head1 AUTHOR
Written by Gabriel Moreau, Grenoble - France
=head1 LICENSE AND COPYRIGHT
GPL version 2 or later and Perl equivalent
Copyright (C) 2005-2017 Gabriel Moreau.