#!/opt/vdops/bin/perl # This script reports the list of active IPSec VPN tunnels on the target device # V Who When What # --------------------------------------------------------------------------- # 1.2.0 skendric 2009-08-14 List both up and down tunnels # 1.1.2 skendric 2009-07-17 Update IP address <--> names hash # 1.1.1 skendric 2008-11-30 Add more IP address <--> name look-ups # 1.1.0 skendric 2008-05-11 Abandon cikeTunRemoteValue and employ # cikePeerLocal/RemoteAddr # 1.0.1 skendric 2008-01-17 More debug output # 1.0.0 skendric 2007-07-02 First Version # # Author: Stuart Kendrick, sbk {put at sign here} skendric {put dot here} com # # Source: http://www.skendric.com/device # # This software is available under the GNU GENERAL PUBLIC LICENSE, see # http://www.fsf.org/licenses/gpl.html # # # This script takes the following approach: # -Accepts a command-line list of targets (if empty, employs a # hard-coded list) # -Queries the targets for IPSec tunnel related parameters # -Produces a report # # # Requirements: # -The following MIB modules stashed in /opt/vdops/share/snmp/mibs, # or wherever it is that you store MIB modules: # CISCO-PRODUCTS-MIB.my # # -PERL modules: the FHCRC::Netops collection # # # Assumptions: # # # Tested on: # -perl-5.10.1 # -net-snmp-5.5 # # # Instructions: # -Customize the script for your site: find the 'user-configurable # variables' section and modify as appropriate # -Type "show-tunnels" to see the command-line options # -Try it out # # # # Caveats: # # # Known Bugs: # # # To do: # -Add support for SNMPv3 # # Begin script # Load modules use strict; use warnings; use feature 'say'; use feature 'switch'; use Carp qw(carp cluck croak confess); use Data::Dumper; use English qw( -no_match_vars ); use Getopt::Std; use Net::IPv4Addr qw(ipv4_chkip); use Switch '__'; use Time::Duration; use FHCRC::Netops::HostTools 1.0.3; use FHCRC::Netops::NetopsTools 2.0.7; use FHCRC::Netops::NetopsData 1.3.0; use FHCRC::Netops::PingTools 1.1.5; use FHCRC::Netops::SNMPTools 1.3.9; use FHCRC::Netops::Utilities 1.3.9; # Declare global variables # All hashes keyed by target and consist of hash refs keyed by tunnel index my %active_tunnels; # Value of cikeGlobalActiveTunnels.0, keyed by # target my %cikeTunIndex; # Tunnel index (keyed by peer name) my %duration; # Length of time across which tunnel has been up my %encrypt_algorithm; # Encryption algorithm my %hash_algorithm; # Hash algorithm my %index_mapping; # Hash refs of hashes of cikeTunIndex keyed # by cipSecTunIkeTunnelIndex (keyed by target) my %lifetime; # Tunnel lifetime my $local_peer; # IP address of our end of the tunnels my %peer_iid_of; # Hash of references to hashes of iids (keyed by # peer), keyed by target my %peer; # IP address of peer my %status; # Up or down my %total_tunnels; # Value of cikeGlobalInitTunnels.0, keyed by # target my %traffic; # Number of bytes which have traversed the tunnel my %tunnel_name; # Hard-coded names of tunnels, indexed by # IP address of tunnel terminator # Define global variables $debug = 0; # 10 = Logging # 9 = Database SELECT operations # 8 = Per IP/MAC/Port processing # 7 = Database INSERT/UPDATE/DELETE # 6 = Dump SNMP var # 5 = Dump snmp_packets # 4 = Grody: print big var # 3 = Verbose: print mid var # 2 = Simple: print small var # 1 = Basic: subroutine trace # 0 = Disable debugging $program_name = 'show-tunnels'; $usage = 'Usage: show-tunnels -s {yes|no} [-d {integer}] [-r] [-a | -e {expr} | -f {filename} | target1 target2 target3 ...]'; $version = '1.2.0'; # Binaries $grab_hosts = '/bin/cat /etc/hosts'; # Pause parameters $long = 30; $mid = 10; $short = 5; # Ping Stuff $ping_count = 3; $ping_timeout = 1; # Report stuff $institution = 'Widgets International'; $report_file = '/home/netops/rpts/show-tunnels.txt'; $report_queries = 'bsmith@widgets.com'; $report_subject = 'IPSec Tunnel Report'; # SNMP Stuff # Optimize performance by sorting your community strings and SNMP version # list, most frequently used to the left, least frequently used to the right @mib_dir = qw(/opt/vdops/share/snmp/mibs); @mib_file = qw/ALL/; @snmp_read_list = qw/public/; @snmp_version_list = qw/2 1/; $snmp_port = 161; $snmp_retries = 3; $snmp_timeout = 5000000; # Syslog stuff $syslog_facility = 'local5'; $syslog_host = 'localhost'; $syslog_port = 514; $syslog_priority = 'info'; $syslog_socket = 'unix'; # Other possibilites include 'udp' and # 'stream'; depending on the flavor of Unix, # I've employed each of these # VPN details $local_peer = '10.112.1.206'; # Target details @skip_name = qw/swamp /; @suffixes = qw/-rtr -vpn/; # Grab arguments getopts('ad:e:f:rs:', \%option); @target = @ARGV; # Domain-specific stuff $option{s} = 'yes' unless defined $option{s}; @target = qw/mmz-a-rtr mmz-b-rtr/ unless @target > 0; %tunnel_name = ( '38.91.254.6' => 'charms', '143.98.254.99' => 'seachild', '213.50.65.4' => 'dejarnette', '208.51.30.230' => 'gems', '128.85.183.12' => 'hematopathology', '140.142.148.5' => 'hmc', '33.238.35.30' => 'icad', '128.85.181.250' => 'impac', '128.208.135.5' => 'mimi', '128.208.190.8' => 'pacs', '33.238.162.4' => 'pms', '209.114.138.110' => 'pyxis', '140.142.23.136' => 'sod', '128.191.150.1' => 'um', ); # Set mode if ($option{r}) { $mode = 'report' } elsif (-t STDIN) { $mode = 'interactive' } else { $mode = 'batch' } ### Begin Main Program ############################################### { check_args(); # Check arguments compile_mibs(); # Compile MIB files build_target(); # Populate @target target_check(); # Look for errors in @target basic_info(); # Gather information do_the_work(); # Do the work print_report(); # Print report } ##### End Main Program ############################################### ######################################################################## # Do the work: acquire image name ######################################################################## sub do_the_work { # Debug trace trace_location('begin') if $debug; # Notify operator print_it('Acquiring tunnel information...'); # Loop through targets TARGET: for my $target (@target) { my (%arg, $val, $vb); say "Processing $target" if $debug; # Acquire cikeGlobalInitTunnels.0 %arg = ( host => $target, oid => 'cikeGlobalInitTunnels.0', translate => 0); $val = snmpGet(\%arg); $val //= 0; say " $target has initiated $val tunnels" if $debug; $total_tunnels{$target} = $val; # Skip uninteresting targets next TARGET unless $total_tunnels{$target} > 0; # Acquire cikeGlobalActiveTunnels.0 say 'Getting cikeGlobalActiveTunnels.0' if $debug > 3; $val = snmpGet( {host => $target, oid => 'cikeGlobalActiveTunnels.0'} ); $val //= 0; say " $target is hosting $val active tunnels" if $debug; $active_tunnels{$target} = $val; # Acquire tunnel peers $vb = snmpWalk( {host => $target, oid => 'cikeTunRemoteValue'} ); for my $varbind (@$vb) { my ($iid, $ip, $peer); $ip = scrub_ip($varbind->{val}); $iid = $varbind->{iid}; $peer = $tunnel_name{$ip} if defined $tunnel_name{$ip}; $peer{$target}->{$iid} = $peer; $cikeTunIndex{$target}->{$peer} = $iid; } if ($debug > 1) { for my $iid (keys %{$peer{$target}}) { say " $target talks to $peer{$target}->{$iid}"; } } # # Acquire tunnel local peers # say 'Walking cikePeerLocalAddr' if $debug > 3; # $vb = snmpWalk( {host=>$target, oid=>' cikePeerLocalAdd', translate=>1} ); # for my $varbind (@$vb) { # my ($iid, $peer); # $peer = scrub_ip($varbind->{val}); # unless ($peer eq $local_peer) { # ($iid) = ($varbind->{iid} =~ /\.(\d+)\Z/); # $peer = $tunnel_name{$peer} if defined $tunnel_name{$peer}; # $peer{$target}->{$iid} = $peer; # $duration{$target}->{$iid} += 0; # $traffic{$target}->{$iid} += 0; # $peer_iid_of{$target}->{$peer} = $iid; # } # } # if ($debug > 3) { # for my $iid (keys %{$peer{$target}}) { # say " $target talks to $peer{$target}->{$iid}"; # } # } # # Acquire tunnel remote peers # $vb = snmpWalk( {host=>$target, oid=>'cikePeerRemoteAddr', translate=>1} ); # for my $varbind (@$vb) { # my ($iid, $peer); # $peer = scrub_ip($varbind->{val}); # unless ($peer eq $local_peer) { # ($iid) = ($varbind->{iid} =~ /\.(\d+)\Z/); # $peer = $tunnel_name{$peer} if defined $tunnel_name{$peer}; # $peer{$target}->{$iid} = $peer; # $duration{$target}->{$iid} += 0; # $traffic{$target}->{$iid} += 0; # $peer_iid_of{$target}->{$peer} = $iid; # } # } # if ($debug > 3) { # for my $iid (keys %{$peer{$target}}) { # say " $target talks to $peer{$target}->{$iid}"; # } # } # # # Look up cipSecTunIkeTunnelIndex # $vb = snmpWalk( {host => $target, oid => 'cipSecTunIkeTunnelIndex'} ); # for my $varbind (@$vb) { # my ($cipSecTunIkeTunnelIndex, $cikeTunIndex); # $cipSecTunIkeTunnelIndex = $varbind->{iid}; # $cikeTunIndex = $varbind->{val}; # #($cikeTunIndex) = ($varbind->{iid}) =~ /\.(\d+)\.\d+\Z/; # $index_mapping{$target}->{$cipSecTunIkeTunnelIndex} = $cikeTunIndex; # } # if ($debug > 1) { # say 'Index mapping'; # for my $iid (sort keys %{$index_mapping{$target}}) { # say "$iid => ", $index_mapping{$target}->{$iid}; # } # } # Acquire tunnel time $vb = snmpWalk( {host => $target, oid => 'cikeTunActiveTime'} ); for my $varbind (@$vb) { my ($iid, $seconds); $seconds = int $varbind->{val} / 100; $iid = $varbind->{iid}; $duration{$target}->{$iid} = $seconds; } # Acquire encryption algorithm $vb = snmpWalk( {host => $target, oid => 'cikeTunEncryptAlgo'} ); for my $varbind (@$vb) { my ($iid, $alg); $alg = $varbind->{val}; $iid = $varbind->{iid}; $encrypt_algorithm{$target}->{$iid} = $alg; } # Acquire hash algorithm $vb = snmpWalk( {host => $target, oid => 'cikeTunHashAlgo'} ); for my $varbind (@$vb) { my ($iid, $hash); $hash = $varbind->{val}; $iid = $varbind->{iid}; $hash_algorithm{$target}->{$iid} = $hash; } # Acquire tunnel lifetime $vb = snmpWalk({host => $target, oid => 'cikeTunLifeTime', translate => 0}); for my $varbind (@$vb) { my ($iid, $lifetime); $lifetime = $varbind->{val}; $iid = $varbind->{iid}; $lifetime{$target}->{$iid} = $lifetime; } # Acquire tunnel status $vb = snmpWalk({host => $target, oid => 'cikeTunStatus'} ); for my $varbind (@$vb) { my ($iid, $status); $status = $varbind->{val}; $iid = $varbind->{iid}; $status{$target}->{$iid} = $status; } # # Acquire tunnel InOctets # $vb = snmpWalk( {host => $target, oid => 'cipSecTunHcInOctets', translate => 0} ); # for my $varbind (@$vb) { # my ($iid, $inoctets, $cikeTunIndex); # $inoctets = $varbind->{val}; # $iid = $varbind->{iid}; # $cikeTunIndex = $index_mapping{$target}->{$iid}; # $traffic{$target}->{$cikeTunIndex} = $inoctets; # } # # # Acquire tunnel OutOctets # $vb = snmpWalk( {host => $target, oid => 'cipSecTunHcInDecompOctets', translte => 0} ); # for my $varbind (@$vb) { # my ($iid, $outoctets, $cikeTunIndex); # $outoctets = $varbind->{val}; # $iid = $varbind->{iid}; # $cikeTunIndex = $index_mapping{$target}->{$iid}; # $traffic{$target}->{$cikeTunIndex} += $outoctets; # } # # # Prettify traffic # for my $iid (keys %{$traffic{$target}}) { # my $octets = $traffic{$target}->{$iid}; # given ($octets) { # when ($_ < 1000) { # $traffic{$target}->{$iid} = $octets . ' B'; # } # when ($_ < 1000000) { # $traffic{$target}->{$iid} = int($octets / 1000) . ' KB'; # } # when ($_ < 1000000000) { # $traffic{$target}->{$iid} = int($octets / 1000000) . ' MB'; # } # when ($_ < 1000000000000) { # $traffic{$target}->{$iid} = int($octets / 1000000000) . ' GB'; # } # when ($_ < 1000000000000000) { # $traffic{$target}->{$iid} = int($octets / 1000000000000) . ' TB'; # } # default { # $traffic{$target}->{$iid} = 'big'; # } # } # } # Entertain operator print $BANG if $mode eq 'interactive'; } # Make things look pretty say "\n" if $mode eq 'interactive'; # Debug info if ($debug > 2) { say 'Dumping %peer'; for my $target (sort keys %peer) { say $target; for my $iid (sort keys %{$peer{$target}}) { say " Iid: $iid => ", $peer{$target}->{$iid}; } } say(''); say 'Dumping %cikeTunIndex'; for my $target (sort keys %peer) { say $target; for my $peer (sort keys %{$cikeTunIndex{$target}}) { say " Peer: $peer => ", $cikeTunIndex{$target}->{$peer}; } } say(''); say 'Dumping %index_mapping'; for my $target (sort keys %peer) { say $target; for my $iid (sort keys %{$index_mapping{$target}}) { say " Iid: $iid => ", $index_mapping{$target}->{$iid}; } } say(''); say 'Dumping %duration'; for my $target (sort keys %peer) { say $target; for my $iid (sort keys %{$duration{$target}}) { say " Duration: $iid => ", $duration{$target}->{$iid}; } } say(''); say 'Dumping %traffic'; for my $target (sort keys %peer) { say $target; for my $iid (sort keys %{$traffic{$target}}) { say " Traffic: $iid => ", $traffic{$target}->{$iid}; } } } # Debug trace trace_location('end') if $debug; return 1; } ######################################################################## # Tell the operator what I discovered ######################################################################## sub print_report { my $handle; my $total = @target; my $now = get_now(); # Debug trace trace_location('begin') if $debug; # If we are running in test mode, skip this routine unless ($dome) { print_it("Running in test mode, cannot print a meaningful report\n"); return 1; } # Direct output to screen or to file if ($mode eq 'interactive') { $handle = *STDOUT; } else { open $handle, '>', $report_file or die "Cannot open $report_file: $!\n"; } if ($mode eq 'report') { print {$handle} <{$peer}; $duration = $duration{$target}->{$iid}; $encrypt = $encrypt_algorithm{$target}->{$iid}; $hash = $hash_algorithm{$target}->{$iid}; $lifetime = $lifetime{$target}->{$iid}; $status = $status{$target}->{$iid}; if ($first == 1) { printf {$handle} "%-16s %4s %4s %8s %6s %8s\n", $peer, $encrypt, $hash, $lifetime, $status, $duration; $first = 0; } else { printf {$handle} "%11s %-16s %4s %4s %8s %6s %8s\n", $SPACE, $peer, $encrypt, $hash, $lifetime, $status, $duration; } } } unless ($handle =~ /STDOUT/) { close $handle or warn "Cannot close $report_file: $!\n"; } # Make things look pretty log_it("Ending $PROGRAM_NAME"); print_it("\n\nEnding $PROGRAM_NAME"); # Debug trace trace_location('end') if $debug; return 1; } ######################################################################## # Output help ######################################################################## sub HELP_MESSAGE { print <