#!/usr/local/bin/perl -s
#


use Socket;

$ippat = '(\d+)\.' x 3 . '(\d+)';
$epat = '([\da-f]+)[\s:-]' x 5 . '([\da-f]+)';


$community = shift @ARGV || &usage();
$switch = shift @ARGV || &usage();
$historydb = shift @ARGV || &usage();


sub usage { 
    die "Usage: $0 <community> <switch>\n";
    }

if ( $switch =~ m/^[\d\.]+$/ ) { 
    $switchip = $switch;
    $switchname = &ip2name( $switch );
    }
else {
    ($a,$b,$c,$d) = unpack('C4',gethostbyname( $switch ));
    $switchip = "$a.$b.$c.$d";
    $switchname = $switch;
    }


&get_system( $switchip, $community ); 
if ( $sysDescr =~ m/Cisco Internetwork Operating System .* Version 12\./ ) { 
    $use_snmpv2 = 1;
    &get_port_info( $switchip, $community ); 
    &get_temp_info( $switchip, $community ); 
    }
elsif ( $sysDescr =~ m/Cisco Catalyst Operating System.* Version 6\./ ) { 
    $use_snmpv2 = 1;
    &get_temp_info( $switchip, $community ); 
    }


&report();

exit 0;

#
# end of main 
##########################




sub snmpwalk { 
    my( $fH, $switch, $community, $oid ) = @_;

    if ( $use_snmpv2 ) { 
	$cmd = "snmpbulkwalk -v 2c -Oq -c $community $switch $oid |";
	}
    else { 
	$cmd = "snmpwalk -v 1 -Oq -c $community $switch $oid |";
	}
    $verbose && print "cmd = '$cmd'\n";
    open( $fH, $cmd ) || die "can't snmpwalk: $!\n";
    }






# get switch's system info
sub get_system { 
    my( $switch, $community ) = @_;

    # why does cisco put cr/lf in the sysDescr???
    &snmpwalk( fH,  $switch, $community, "system.sysDescr" );
    while ( <fH> ) { 
	chomp;
	s/\s+$/ /;
	if ( m/sysDescr\.0 (.*)/ ) { 
	    $sysDescr = $1;
	    }
	else { 
	    $sysDescr .= $_;
	    }
	}
    close fH;
    $verbose && print "sysDescr = $sysDescr\n";

    &snmpwalk( fH,  $switch, $community, "system.sysName" );
    while ( <fH> ) { 
	if ( m/sysName\.0 (.*)/ ) { 
	    $sysName = $1;
	    $verbose && print "sysName = $sysName\n";
	    }
	}
    close fH;
    }

    




# get switch's port info
sub get_port_info { 
    my( $switch, $community ) = @_;

    # from RFC1213-MIB.my
    # interfaces table
    &snmpwalk( fH,  $switch, $community, "interfaces.ifTable.ifEntry" );
    while ( <fH> ) { 
	next unless ( m/(\w+)\.(\d+) (.+)/ );
	$field = $1;
	$ifIndex = $2;
	$val = $3;
	$interfaces{ $field }{ $ifIndex } = $val;
	}

    }



sub get_temp_info { 
    my( $switch, $community ) = @_;


    # pull entityMIB.entityMIBObjects.entityPhysical.entPhysicalTable
    # for names of sensors
    &snmpwalk( fH,  $switch, $community, "1.3.6.1.2.1.47.1.1.1.1.2" );
    while ( <fH> ) { 
	next unless ( m/\.(\d+) (.+)/ );
	$col = "entPhysicalDescr";
	$row = $1;
	$val = $2;
	$entPhysicalDescr{ $row } = $val;
	}
    close fH;

    # try the ciscoEnvMonMIB
    # works on 7206s, and chp-br4
    # works on 3648, but only get status, not temperature value 
    #
    &snmpwalk( fH,  $switch, $community, "1.3.6.1.4.1.9.9.13.1.3.1" );
    while ( <fH> ) { 
	next unless ( m/(\w+)\.(\d+) (.+)/ );
	$col = $1;
	$row = $2;
	$val = $3;
	$envmon_mib{ $row }{ $col } = $val;
	}
    close fH;

    foreach $row ( sort { $a <=> $b } keys %envmon_mib ) { 
	if ( defined( $envmon_mib{ $row }{ 3 } ) ) { 
	    $temp = $envmon_mib{ $row }{ 3 };
	    if ( $temp > $critical_temp ) { 
		printf "CRITICAL %s = %d C\n", $envmon_mib{ $row }{ 2 }, 
		    $envmon_mib{ $row }{ 3 };
		exit 2;
		}
	    elsif ( $temp > $warning_temp ) { 
		printf "WARNING %s = %d C\n", $envmon_mib{ $row }{ 2 }, 
		    $envmon_mib{ $row }{ 3 };
		exit 1;
		}
	    else { 
		printf "OK %s = %d C\n", $envmon_mib{ $row }{ 2 }, 
		    $envmon_mib{ $row }{ 3 };
		}
	    }
	elsif ( defined( $envmon_mib{ $row }{ 6 } ) ) {
	    $state = $envmon_mib{ $row }{ 6 };
	    # snmp values: 1 ok, 2 warning, 3 critical, 4 shutdown, 
	    #     5 not present, 6 not functioning
	    # nagios return codes: -1 unknown, 0 ok, 1 warning, 2 critical
	    if ( $state == 3 ) { 
		printf "CRITICAL %s\n", $envmon_mib{ $row }{ 2 };
		exit 2;
		}
	    elsif ( $state == 2 ) { 
		printf "WARNING %s\n", $envmon_mib{ $row }{ 2 };
		exit 1;
		}
	    else { 
		printf "OK %s\n", $envmon_mib{ $row }{ 2 };
		}
	    }
	}


    # try the entitySensorMIB
    # this works on catalyst 6509
    &snmpwalk( fH,  $switch, $community, "1.3.6.1.4.1.9.9.91.1.1" );
    while ( <fH> ) { 
	next unless ( m/(\w+)\.(\d+) (.+)/ );
	$col = $1;
	$row = $2;
	$val = $3;
	$sensor_mib{ $row }{ $col } = $val;
	}
    close fH;

    foreach $row ( sort { $a <=> $b } keys %sensor_mib ) { 
	if ( $sensor_mib{ $row }{ 1 } == 8   # type celsius
		&& $sensor_mib{ $row }{ 5 } == 1  )  {  # sensor status ok
	    $temp = $sensor_mib{ $row }{ 4 };
	    if ( $temp > $critical_temp ) { 
		printf "CRITICAL %s = %d C\n", $entPhysicalDescr{ $row }, $temp;
		exit 2;
		}
	    elsif ( $temp > $warning_temp ) { 
		printf "WARNING %s = %d C\n", 
		exit 1;
		}
	    else { 
		printf "OK %s = %d C\n", $envmon_mib{ $row }{ 2 }, 
		    $envmon_mib{ $row }{ 3 };
		}
	    }
	    printf "%s: %d C\n", $entPhysicalDescr{ $row } }, 
		$sensor_mib{ $row }{ 4 };
	    }
	}





sub ip2name { 
    my( $ipaddr ) = @_;
    $ipaddr =~ m/$ippat/o;
    $themaddr = pack( "C4", $1, $2, $3, $4 );
    $host = gethostbyaddr( $themaddr, &AF_INET );
    $host = "unknown" if ! $host;
    return $host;
    }
    






sub report { 

    foreach $port ( sort keys %cam_table ) { 
	$verbose && print "port $port\n";

	#next if ( ! defined( $ifIndeces{ $port } ) );
	$ifIndex = $ifIndeces{ $port };
	$verbose && print "ifIndex $ifIndex\n";

	$ifOperStatus = $interfaces{ ifOperStatus }{ $ifIndex };
	next if ( $ifOperStatus eq "down" && ! $verbose );

	$ifType = $interfaces{ ifType }{ $ifIndex };
	next if ( $ifType eq "other" && ! $verbose );

	# count of mac addrs on port
	$count = scalar( keys %{$cam_table{ $port }} );
	# -1 for port's own mac
	if ( defined( $interfaces{ ifPhysAddress }{ $ifIndex } ) ) { 
	    $count --;
	    }
	next if ( $count < 1 && ! $verbose );

	$ifDescr = $interfaces{ ifDescr }{ $ifIndex };
	next if ( $ifDesc eq "Null0" );  # discard port
	$ifDescr =~ s/GigabitEthernet/g/;
	$ifDescr =~ s/FastEthernet/f/;
	$ifDescr =~ s/Serial/s/;
	
	$ifSpeed = sprintf( "%dMb", $interfaces{ ifSpeed }{ $ifIndex } / 1000000 );
	$ifAdminStatus = $interfaces{ ifAdminStatus }{ $ifIndex };

	$ifPhysAddress = $interfaces{ ifPhysAddress }{ $ifIndex };
	$ifPhysAddress =~ s/\b[\da-f]\b/0$&/gi;
	$verbose && print "ifPhysAddress $ifPhysAddress \n"; 

	$modport = $modports{ $ifIndex };

	print "$switchname $switchip $modport '$ifDescr' $ifSpeed $ifAdminStatus $ifOperStatus [$count]\n";


	foreach $eaddr ( keys %{$cam_table{ $port }} ) { 
	    next if ( $eaddr eq $ifPhysAddress );
	    $vlan = $cam_table{ $port }{ $eaddr };
	    #next if ( ! $verbose && $vlan == 1 );
	    if ( defined( $ipaddrs{ $eaddr } ) ) { 
		$ipaddr = $ipaddrs{ $eaddr };
		$host = &ip2name( $ipaddr );
		}
	    else { 
		$ipaddr = "no-ip";
		$host = "unknown";
		}
	    printf "    %-17s  %3d  %-15s  %s\n", $eaddr, $vlan, $ipaddr, $host;
	    }
	}
    }




