#!/usr/bin/perl -wT
use strict;
$| = 1;
#
# cislog-report.pl - summarize Cicso device syslog messages
#
# Copyright (C) 2003-2004 John Kristoff
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License or any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public LICENSE
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
#
# Author: John Kristoff <jtk@northwestern.edu>
#         Northwestern University
#         Evanston, IL USA
#
# Usage: ./cislog-report.pl device.log
#    or: ./cislog-report.pl < device.log
#    or: STDOUT | ./cislog-report.pl
#
# Needs a Cisco device that generates a syslog file as an argument
# or as input via STDIN.  This will produce a summary of log messages.
# View the README file for further information and instructions.
#
# 2003-07-24,jtk: Initial coding and testing
# 2003-12-23,jtk: online doc, fixed icmp reports, minor edits, added DEBUG
#                 output at program end run, added ch_process_count report
# 2004-06-16,jtk: updated contact info and copyright date

# --- Begin Configuration Section --- #

my $DEBUG  = 0;     # 0 = disabled, 1 = enabled (low), 2 = enabled (high)
my $RESOLVE = 1;    # 0 = disabled, 1 = enabled (resolve IPs to hostname)
my $TOPx = 10;      # max no. of rows for each report (5-25 recommended)

# --- End Configuration Section --- #

use Socket;             # for gethostbyaddr (resolving IPs to names)
use Cwd;                # for getcwd (get current working directory)

# --- Global vars --- #
my $VERSION = 1.2;
my $MAN = "http://aharp.ittns.northwestern.edu/software/cislog-1.2.man.html";

my @arguments = @ARGV;  # save arguments (log files) from shift in <> loop

my $DEBUG_start_time = 0;
if ($DEBUG) {
    $DEBUG_start_time = localtime;
    chomp($DEBUG_start_time);
    print "[DEBUG] debug mode enabled\n";
}

# initialize log hash arrays
my %device_count = ();
my %tp_device_count = ();
my %smf_device_count = ();
my %sr_device_total = ();
my %dlp_acl_count = ();
my %dlp_src_count = ();
my %dlp_dst_port_count = ();
my %dlp_device_total = ();
my %dld_acl_count = ();
my %dld_src_count = ();
my %dld_dst_port_count = ();
my %dld_device_total = ();
my %ald_acl_count = ();
my %ald_proto_count = ();
my %ald_src_count = ();
my %ald_dst_count = ();
my %ald_src_port_count = ();
my %ald_dst_port_count = ();
my %ald_icmptype_count = ();
my %ald_icmpcode_count = ();
my %ald_icmp_count = ();
my %ald_device_total = ();
my %alp_acl_count = ();
my %alp_proto_count = ();
my %alp_src_count = ();
my %alp_src_port_count = ();
my %alp_dst_count = ();
my %alp_dst_port_count = ();
my %alp_icmptype_count = ();
my %alp_icmpcode_count = ();
my %alp_icmp_count = ();
my %alp_device_total = ();
my %oac_nbr_count = ();
my %oac_startstate_count = ();
my %oac_endstate_count = ();
my %oac_device_total = ();
my %dc_src_count = ();
my %dc_device_total = ();
my %ch_process_count = ();
my %ch_device_total = ();
my %rc_src_count = ();
my %rc_device_total = ();
my %lu_interface_count = ();
my %lu_state_count = ();
my %lu_device_total = ();
my %lpu_interface_count = ();
my %lpu_state_count = ();
my %lpu_device_total = ();
my %rdlf_interface_count = ();
my %rdlf_times_count = ();
my %rdlf_device_total = ();
my %le_interface_count = ();
my %le_device_total = ();
my %rdaf_interface_count = ();
my %rdaf_addresses_count = ();
my %rdaf_device_total = ();
my %lc_interface_count = ();
my %lc_state_count = ();
my %lc_device_total = ();
my %papfs_device_total = ();
my %papts_device_total = ();
my %psr_devmodportmac_total = ();
my %psr_device_total = ();
my %mrjr_device_count = ();
my %mrir_device_count = ();
my %tfmsa_device_count = ();
my %other_type_count = ();
my %other_device_total = ();

# initialize log message totals
my $tp_total = 0;           # total traceback/process messages
my $smf_total = 0;          # total system malloc failure messages
my $sr_total = 0;           # total system reset/reload messages
my $dlp_total = 0;          # total device login permits
my $dld_total = 0;          # total device login denies
my $ald_total = 0;          # total access list denies
my $alp_total = 0;          # total access list permits
my $oac_total = 0;          # total ospf adjacency changes
my $dc_total = 0;           # total device configured messages
my $ch_total = 0;           # total cpuhog messages
my $rc_total = 0;           # total rshell command attempts
my $lu_total = 0;           # total link updown messages
my $lpu_total = 0;          # total line protocol updown messages
my $rdlf_total = 0;         # total runtime diagnostic link flaps
my $le_total = 0;           # total link errors
my $rdaf_total = 0;         # total runtime diagnostic address flaps
my $lc_total = 0;           # total link changes
my $papfs_total = 0;        # port aggregation protocol from STP
my $papts_total = 0;        # port aggregation protocol to STP
my $psr_total = 0;          # port security reject
my $mrjr_total = 0;         # multicast rx join range messages
my $mrir_total = 0;         # multicast rx IGMP report messages
my $tfmsa_total = 0;        # invalid multicast traffic from source address
my $other_total = 0;        # total other (uncategorized) log messages
my $unknown_total = 0;      # total unknown log messages

my $log_count = 0;          # total log message counter
my @logs_per_hour = (0, 0, 0, 0, 0, 0, 0, 0,
                     0, 0, 0, 0, 0, 0, 0, 0,
                     0, 0, 0, 0, 0, 0, 0, 0);

# Pass cisco syslog file as script argument or send log file to
# to script via unix STDIN.
while (defined(my $log = <>)) {

    chomp $log;
    $log_count++;
    
    # parse basic format of syslog log file
    my ($sysmonth, $sysday, $systime, $device, $syslogid, $msg)
        = split (/\s+/, $log, 6);

    $device_count{$device}++;

    my $hour = 0;
    ($hour) =  $systime =~ /^(\d\d)/; 
    # verify that we have something in the range of 00 - 23
    if ($hour < 0 || $hour > 23 || $hour !~ /\d\d/) {
	die "Out of bounds hour reference!\n";
    }
    $logs_per_hour[$hour]++;

    SWITCH: { # phoney switch to parse on each $msg =~ /^<regexp>/i

        # traceback/process 
        #
        # [-Process|-Trackback]=[data]

        if ($msg =~ 
	    /^(\-Process|\-Traceback)\=.*/i
	   ) {

	    $tp_device_count{$device}++;
            $tp_total++;

	    next;
	}

        # system malloc failure
        #
        # %SYS-2-MALLOCFAIL: Memory allocation of [#] bytes failed from
        # [hex address], alignment [#]

        if ($msg =~
            /\%SYS-2-MALLOCFAIL\:\s+Memory\s+allocation\s+of\s+\d+\s+bytes\s+failed\s+from\s+0x\d+\,\s+alignment\s+0/i
           ) {

            $smf_device_count{$device}++;
            $smf_total++;

            next;
        }

        # system reset/reload
        #
        # %SYS-5-SYS_RESET:System reset from [source]        # catalyst switch
        # %SYS-5-RELOAD: Reload requested[text and details]  # IOS router

        if ($msg =~
            /\%SYS-5-SYS_RESET\:System\s+reset\s+from\s+\S+/i
           ) {

            $sr_device_total{$device}++;
            $sr_total++;

            next;
        }

        if ($msg =~
            /\%SYS-5-RELOAD:\s+Reload\s+requested.*/i
           ) {

            $sr_device_total{$device}++;
            $sr_total++;

            next;
        }

        # device login attempts
        #
        # %SEC-6-IPACCESSLOGP: list [acl] [denied|permitted] tcp [ip]([port])
        # [optional input interface] -> 0.0.0.0(22|23), [count] packet[s]

        if ($msg =~
            /\%SEC-6-IPACCESSLOGP\:\s+list\s+\S+\s+(denied|permitted)\s+tcp\s+\d+\.\d+\.\d+\.\d+\(\d+\).*\-\>\s+0\.0\.0\.0\((22|23)\)\,\s+\d+\s+packet/i
           ) {

            my ( $alh_acl, $alh_status, $alh_src, $alh_port, $alh_count ) =
               ( 0,        0,           0,        0,         0 );

	    ($alh_acl, $alh_status, $alh_src, $alh_port, $alh_count ) =
              $msg =~
		/\%SEC-6-IPACCESSLOGP\:\s+list\s+
                (\S+)                                      # alh_acl
                \s+
                (denied|permitted)                         # alh_status
                \s+tcp\s+
                (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})       # alh_src
                \(\d+\).*\-\>\s+0\.0\.0\.0\(
                (22|23)                                    # alh_port
                \)\,\s+
                (\d+)                                      # alh_count
                \s+packet/ix;

            # device login denies
            if ($alh_status =~ /denied/) {
		$dld_acl_count{$alh_acl} += $alh_count;
		$dld_src_count{$alh_src} += $alh_count;
		$dld_dst_port_count{$alh_port} += $alh_count;
		$dld_device_total{$device} += $alh_count;
		$dld_total += $alh_count;
            }
            # device login permits
            else {
		$dlp_acl_count{$alh_acl} += $alh_count;
		$dlp_src_count{$alh_src} += $alh_count;
		$dlp_dst_port_count{$alh_port} += $alh_count;
		$dlp_device_total{$device} += $alh_count;
		$dlp_total += $alh_count;
            }

            next;
        }

        # access list hits - multiple log message formats

        # IPACCESSLOGP - tcp/udp
        # ------------
        # %SEC-6-IPACCESSLOGP: list [acl] [denied|permitted] [tcp|udp]
        # [ip]([port]) [optional input interface] -> [ip]([port]),
        # [count] packet[s]

        if ($msg =~
            /\%SEC-6-IPACCESSLOGP\:\s+list\s+\S+\s+(denied|permitted)\s+(udp|tcp)\s+\d+\.\d+\.\d+\.\d+\(\d+\).*\-\>\s+\d+\.\d+\.\d+\.\d+\(\d+\)\,\s+\d+\s+packet/i
           ) {

             my( $alh_acl, $alh_status, $alh_proto, $alh_src,
                 $alh_src_port, $alh_dst, $alh_dst_port, $alh_count )
              = ( 0, 0, 0, 0, 0, 0, 0, 0 );

             ($alh_acl, $alh_status, $alh_proto, $alh_src, $alh_src_port,
              $alh_dst, $alh_dst_port, $alh_count) = $msg =~
		/\%SEC-6-IPACCESSLOGP\:\s+list\s+
                (\S+)                                      # alh_acl
                \s+
                (denied|permitted)                         # alh_status
                \s+
                (tcp|udp)                                  # alh_proto
                \s+
                (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})       # alh_src 
                \(
                (\d+)                                      # alh_src_port
                \).*\-\>\s+
                (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})       # alh_dst
                \(
                (\d+)                                      # alh_dst_port
                \)\,\s+
                (\d+)                                      # alh_count
                \s+packet/ix;

            # access list denies
            if ($alh_status =~ /denied/) {
		$ald_acl_count{$alh_acl} += $alh_count;
		$ald_proto_count{$alh_proto} += $alh_count;
		$ald_src_count{$alh_src} += $alh_count;
                $ald_src_port_count{$alh_src_port} += $alh_count;
		$ald_dst_count{$alh_dst} += $alh_count;
		$ald_dst_port_count{$alh_dst_port} += $alh_count;
		$ald_device_total{$device} += $alh_count;
		$ald_total += $alh_count;
            }
            # access list permits
            else {
		$alp_acl_count{$alh_acl} += $alh_count;
		$alp_proto_count{$alh_proto} += $alh_count;
		$alp_src_count{$alh_src} += $alh_count;
                $alp_src_port_count{$alh_src_port} += $alh_count;
		$alp_dst_count{$alh_dst} += $alh_count;
		$alp_dst_port_count{$alh_dst_port} += $alh_count;
		$alp_device_total{$device} += $alh_count;
		$alp_total += $alh_count;
            }

            next;
        }

        # IPACCESSLOGNP - unknown protocol (e.g. 41, 50)
        # -------------
        # %SEC-IP-IPACCESSLOGNP: list [acl] [denied|permitted] [protocol] [ip]
        # [optional input interface] -> [ip], [count] packet[s]

        if ($msg =~
            /\%SEC-6-IPACCESSLOGNP\:\s+list\s+\S+\s+(denied|permitted)\s+\d+\s+\d+\.\d+\.\d+\.\d+\s+.*\-\>\s+\d+\.\d+\.\d+\.\d+\,\s+\d+\s+packet/i
           ) {

            my ( $alh_acl, $alh_status, $alh_proto, $alh_src, $alh_dst,
                 $alh_count ) = ( 0, 0, 0, 0, 0, 0 );

            ($alh_acl, $alh_status, $alh_proto, $alh_src, $alh_dst,
             $alh_count) = $msg =~
		/\%SEC-6-IPACCESSLOGNP\:\s+list\s+
                (\S+)                                      # alh_acl
                \s+
                (denied|permitted)                         # alh_status
                \s+
                (\d+)                                      # alh_proto
                \s+
                (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})       # alh_src 
                \s+.*\-\>\s+
                (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})       # alh_dst
                \,\s+
                (\d+)                                      # alh_count
                \s+packet/ix;

            # access list denies
            if ($alh_status =~ /denied/) {
                $ald_acl_count{$alh_acl} += $alh_count;
                $ald_proto_count{getprotobynumber($alh_proto) or $alh_proto}
                    += $alh_count;
		$ald_src_count{$alh_src} += $alh_count;
		$ald_dst_count{$alh_dst} += $alh_count;
		$ald_device_total{$device} += $alh_count;
		$ald_total += $alh_count;
            }
            # access list permits
            else {
                $alp_acl_count{$alh_acl} += $alh_count;
                $alp_proto_count{getprotobynumber($alh_proto) or $alh_proto}
                    += $alh_count;
		$alp_src_count{$alh_src} += $alh_count;
		$alp_dst_count{$alh_dst} += $alh_count;
		$alp_device_total{$device} += $alh_count;
		$alp_total += $alh_count;
            }

            next;
        }

        # IPACCESSLOGRP - known protocols (e.g. igmp, gre)
        # -------------
        # %SEC-6-IPACCESSLOGRP: list [acl] [denied|permitted] [protocol] [ip] \
        # [optional input interface] -> [ip], [count] packet[s]

        if ($msg =~
            /\%SEC-6-IPACCESSLOGRP\:\s+list\s+\S+\s+(denied|permitted)\s+\S+\s+\d+\.\d+\.\d+\.\d+\s+.*\-\>\s+\d+\.\d+\.\d+\.\d+\,\s+\d+\s+packet/i
           ) {

            my ( $alh_acl, $alh_status, $alh_proto, $alh_src, $alh_dst,
                 $alh_count ) = ( 0, 0, 0, 0, 0, 0 );

            ($alh_acl, $alh_status, $alh_proto, $alh_src, $alh_dst,
             $alh_count) = $msg =~
		/\%SEC-6-IPACCESSLOGRP\:\s+list\s+
                (\S+)                                      # alh_acl
                \s+
                (denied|permitted)                         # alh_status
                \s+
                (\S+)                                      # alh_proto
                \s+
                (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})       # alh_src 
                .*\-\>\s+
                (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})       # alh_dst
                \,\s+
                (\d+)                                      # alh_count
                \s+packet/ix;

            # access list denies
            if ($alh_status =~ /denied/) {
		$ald_acl_count{$alh_acl} += $alh_count;
		$ald_proto_count{$alh_proto} += $alh_count;
		$ald_src_count{$alh_src} += $alh_count;
		$ald_dst_count{$alh_dst} += $alh_count;
		$ald_device_total{$device} += $alh_count;
		$ald_total += $alh_count;
            }
            # access list permits
            else {
		$alp_acl_count{$alh_acl} += $alh_count;
		$alp_proto_count{$alh_proto} += $alh_count;
		$alp_src_count{$alh_src} += $alh_count;
		$alp_dst_count{$alh_dst} += $alh_count;
		$alp_device_total{$device} += $alh_count;
		$alp_total += $alh_count;
            }

            next;
        }

        # IPACCESSLOGDP - icmp
        # -------------
        # %SEC-6-IPACCESSLOGDP: list [acl] [denied|permitted] [protocol] [ip] \
        # [optional input interface] -> [ip] ([type]/[code]), [count] packet[s]

        if ($msg =~
            /\%SEC-6-IPACCESSLOGDP\:\s+list\s+\S+\s+(denied|permitted)\s+icmp\s+\d+\.\d+\.\d+\.\d+\s+.*\-\>\s+\d+\.\d+\.\d+\.\d+\s+\(\d+\/\d+\)\,\s+\d+\s+packet/i
           ) {

             my ( $alh_acl, $alh_status, $alh_proto, $alh_src, $alh_dst,
                  $alh_icmptype, $alh_icmpcode, $alh_count ) =
                ( 0, 0, 0, 0, 0, 0, 0, 0 );

             ($alh_acl, $alh_status, $alh_proto, $alh_src, $alh_dst,
              $alh_icmptype, $alh_icmpcode, $alh_count) = $msg =~
                /\%SEC-6-IPACCESSLOGDP\:\s+list\s+
                (\S+)                                      # alh_acl
                \s+
                (denied|permitted)                         # alh_status
                \s+
                (\S+)                                      # alh_proto
                \s+
                (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})       # alh_src 
                \s+.*\-\>\s+
                (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})       # alh_dst
                \s+\(
                (\d+)                                      # alh_icmptype
                \/
                (\d+)                                      # alh_icmpcode
                \)\,\s+
                (\d+)                                      # alh_count
                \s+packet/ix;

            # access list denies
            if ($alh_status =~ /denied/) {
		$ald_acl_count{$alh_acl} += $alh_count;
		$ald_proto_count{$alh_proto} += $alh_count;
		$ald_src_count{$alh_src} += $alh_count;
		$ald_dst_count{$alh_dst} += $alh_count;
		$ald_icmptype_count{$alh_icmptype} += $alh_count;
		$ald_icmpcode_count{$alh_icmpcode} += $alh_count;
		$ald_icmp_count{$alh_icmptype . "/" . $alh_icmpcode}
                    += $alh_count;
		$ald_device_total{$device} += $alh_count;
		$ald_total += $alh_count;
            }
            # access list permits
            else {
		$alp_acl_count{$alh_acl} += $alh_count;
		$alp_proto_count{$alh_proto} += $alh_count;
		$alp_src_count{$alh_src} += $alh_count;
		$alp_dst_count{$alh_dst} += $alh_count;
		$alp_icmptype_count{$alh_icmptype} += $alh_count;
		$alp_icmpcode_count{$alh_icmpcode} += $alh_count;
		$alp_icmp_count{$alh_icmptype . "/" . $alh_icmpcode}
                    += $alh_count;
		$alp_device_total{$device} += $alh_count;
		$alp_total += $alh_count;
            }

            next;
        }

        # IPACCESSLOGS - device sourced packet
        # ------------
        # %SEC-6-IPACCESSLOGS: list [acl] [denied|permitted] [ip] [count]
        # packet[s]

        if ($msg =~
            /\%SEC-6-IPACCESSLOGS\:\s+list\s+\S+\s+(denied|permitted)\s+\d+\.\d+\.\d+\.\d+\s+\d+\s+packet/i
           ) {

            my ( $alh_acl, $alh_status, $alh_src, $alh_count ) = ( 0, 0, 0, 0 );

            ($alh_acl, $alh_status, $alh_src, $alh_count) = $msg =~
                /\%SEC-6-IPACCESSLOGS\:\s+list\s+
                (\S+)                                      # alh_acl
                \s+
                (denied|permitted)                         # alh_status
                \s+
                (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})       # alh_src
                \s+
                (\d+)                                      # alh_count
                \s+packet/ix;

            # access list denies
            if ($alh_status =~ /denied/) {
		$ald_acl_count{$alh_acl} += $alh_count;
		$ald_src_count{$alh_src} += $alh_count;
		$ald_device_total{$device} += $alh_count;
		$ald_total += $alh_count;
            }
            # access list permits
            else {
		$alp_acl_count{$alh_acl} += $alh_count;
		$alp_src_count{$alh_src} += $alh_count;
		$alp_device_total{$device} += $alh_count;
		$alp_total += $alh_count;
            }

            next;
        }

        # ospf adjacency change
        #
        # %OSPF-5-ADJCHG: Process [process], Nbr [ip] on [interface] from
        # [state] to [state], [description]
        # states: DOWN, INIT, 2WAY, EXSTART, EXCHANGE, LOADING, FULL

        if ($msg =~
            /\%OSPF-5-ADJCHG\:\s+Process\s+\d+\,\s+Nbr\s+\d+\.\d+\.\d+\.\d+\s+on\s+\S+\s+from\s+(DOWN|INIT|2WAY|EXSTART|EXCHANGE|LOADING|FULL)\s+to\s+(DOWN|INIT|2WAY|EXSTART|EXCHANGE|LOADING|FULL)\,\s+.*/i
           ) {
            my ($oac_nbr, $oac_startstate, $oac_endstate) = (0, 0, 0);

            ($oac_nbr, $oac_startstate, $oac_endstate) = $msg =~
                /\%OSPF-5-ADJCHG\:\s+Process\s+\d+\,
                \s+Nbr\s+
                (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})       # oac_nbr
                \s+on\s+\S+\s+from\s+
                (\S+)                                      # oac_startstate
                \s+to\s+
                (\S+)                                      # oac_endstate
                \,\s+.*/ix;

            $oac_nbr_count{$oac_nbr}++;
            $oac_startstate_count{$oac_startstate}++;
            $oac_endstate_count{$oac_endstate}++;
            $oac_device_total{$device}++;
            $oac_total++;

            next;
        }

        # device configured
        #
        # %SYS-5-CONFIG_I: Configured from console by vty[#] ([ip])
        # %SYS-5-CONFIG_I: Configured from console by console

        if ($msg =~
            /\%SYS-5-CONFIG_I\:\s+Configured\s+from\s+console\s+by\s+vty\d+\s+\(\d+\.\d+\.\d+\.\d+\)/i
           ) {
            my ($dc_src) = (0);

            ($dc_src) = $msg =~
                /\%SYS-5-CONFIG_I\:\s+Configured\s+from
                \s+console\s+by\s+vty\d+\s+\(
                (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})       # dc_src
                \)/ix;

            $dc_src_count{$dc_src}++;
            $dc_device_total{$device}++;
            $dc_total++;

            next;
        }

        if ($msg =~
            /\%SYS-5-CONFIG_I\:\s+Configured\s+from\s+console\s+by\s+console/i
           ) {

            $dc_src_count{"console"}++;
            $dc_device_total{$device}++;
            $dc_total++;

            next;
        }

        # cpu hog
        #
        # %SYS-3-CPUHOG: Task ran for [msec] msec ([dec]/[dec]), process =
        # [PROCESS Description], PC = [hex]

        if ($msg =~
            /\%SYS-3-CPUHOG\:\s+Task\s+ran\s+for\s+\d+\s+msec\s+\(\d+\/\d+\)\,\s+process\s+\=\s+.*\,\s+PC\s+\=\s+\S+/i
           ) {
            my ($ch_process) = (0);

            ($ch_process) = $msg =~
                /\%SYS-3-CPUHOG\:\s+Task\s+ran\s+for
                \s+\d+\s+msec\s+\(\d+\/\d+\)\,\s+process
                \s+\=\s+
                (.*)                                       # ch_process
                \,\s+PC\s+\=\s+\S+/ix;

            $ch_process_count{$ch_process}++;
            $ch_device_total{$device}++;
            $ch_total++;

            next;
        }

        # rshell command
        #
        # %RCMD-4-RSHPORTATTEMPT: Attempted to connect to RSHELL from [ip]

        if ($msg =~
            /\%RCMD-4-RSHPORTATTEMPT\:\s+Attempted\s+to\s+connect\s+to\s+RSHELL\s+from\s+\d+\.\d+\.\d+\.\d+/i
           ) {
            my ($rc_src) = (0);

            ($rc_src) = $msg =~
                /\%RCMD-4-RSHPORTATTEMPT\:\s+Attempted
                \s+to\s+connect\s+to\s+RSHELL\s+from\s+
                (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})       # rc_src
                /ix;

            $rc_src_count{$rc_src}++;
            $rc_device_total{$device}++;
            $rc_total++;

            next;
        }

        # link updown 
        #
        # %LINK-3-UPDOWN: Interface [interface], changed state to [state] 

        if ($msg =~
            /\%LINK-3-UPDOWN\:\s+Interface\s+\S+\,\s+changed\s+state\s+to\s+[up|down]/i
           ) {
            my ($lu_interface, $lu_state) = (0, 0);

            ($lu_interface, $lu_state) = $msg =~
                /\%LINK-3-UPDOWN\:\s+Interface\s+
                (.*)                                        # lu_interface
                \,\s+changed\s+state\s+to\s+
                ([up|down])                                 # lu_state
                /ix;

            $lu_interface_count{$device}{$lu_interface}++;
            $lu_state_count{$lu_state}++;
            $lu_device_total{$device}++;
            $lu_total++;

            next;
        }

        # line protocol change
        #
        # %LINEPROTO-5-UPDOWN: Line protocol on Interface [interface],
        # changed state to [down|up]

        if ($msg =~
            /\%LINEPROTO-5-UPDOWN\:\s+Line\s+protocol\s+on\s+Interface\s+\S+\,\s+changed\s+state\s+to\s+(down|up)/i
           ) {
            my ($lpu_interface, $lpu_state) = (0, 0);

            ($lpu_interface, $lpu_state) = $msg =~
                /\%LINEPROTO-5-UPDOWN\:\s+Line\s+
                protocol\s+on\s+Interface\s+
                (.*)                                        # lpu_interface
                \,\s+changed\s+state\s+to\s+
                (down|up)                                   # lpu_state
                /ix;

            $lpu_interface_count{$device}{$lpu_interface}++;
            $lpu_state_count{$lpu_state}++;
            $lpu_device_total{$device}++;
            $lpu_total++;

            next;
        }

        # runtime diagnostic link flap
        #
        # %RTD-1-LINK_FLAP: [interface] link down/up [times] times per min 

        if ($msg =~
            /\%RTD-1-LINK_FLAP\:\s+\S+\s+link\s+down\/up\s+\d+\s+times\s+per\s+min/i
           ) {
            my ($rdlf_interface, $rdlf_times) = (0, 0);

            ($rdlf_interface, $rdlf_times) = $msg =~
                /\%RTD-1-LINK_FLAP\:\s+
                (.*)                                        # rdlf_interface
                \s+link\s+down\/up\s+
                (\d+)                                       # rdlf_times
                \s+times\s+per\s+min/ix;

            $rdlf_interface_count{$device}{$rdlf_interface}++;
            $rdlf_times_count{$rdlf_times}++;
            $rdlf_device_total{$device}++;
            $rdlf_total++;

            next;
        }

        # link error
        #
        # %LINK-4-ERROR: [interface] is experiencing errors

        if ($msg =~
            /\%LINK-4-ERROR\:\s+\S+\s+is\s+experiencing\s+errors/i
           ) {
            my ($le_interface) = (0);

            ($le_interface) = $msg =~
                /\%LINK-4-ERROR\:\s+
                (\S+)                                       # le_interface
                \s+is\s+experiencing\s+errors/ix;

            $le_interface_count{$device}{$le_interface}++;
            $le_device_total{$device}++;
            $le_total++;

            next;
        }

        # runtime diagnostic address flap
        #
        # %RTD-1-ADDR_FLAP: [interface] relearning [addresses] addrs per min

        if ($msg =~
            /\%RTD-1-ADDR_FLAP\:\s+.*\s+relearning\s+\d+\s+addrs\s+per\s+min/i
           ) {
            my ($rdaf_interface, $rdaf_addresses) = (0, 0);

            ($rdaf_interface, $rdaf_addresses) = $msg =~
                /\%RTD-1-ADDR_FLAP\:\s+
                (.*)                                        # rdaf_interface
                \s+relearning\s+
                (\d+)                                       # rdaf_addresses
                \s+addrs\s+per\s+min/ix;

            $rdaf_interface_count{$device}{$rdaf_interface}++;
            $rdaf_addresses_count{$rdaf_addresses}++;
            $rdaf_device_total{$device}++;
            $rdaf_total++;

            next;
        }

        # link changed 
        #
        # %LINK-5-CHANGED: Interface [interface], changed state to [state]

        if ($msg =~
            /\%LINK-5-CHANGED\:\s+Interface\s+.*\,\s+changed\s+state\s+to\s+.*/i
           ) {
            my ($lc_interface, $lc_state) = (0, 0);

            ($lc_interface, $lc_state) = $msg =~
                /\%LINK-5-CHANGED\:\s+Interface\s+
                (.*)                                        # lc_interface
                \,\s+changed\s+state\s+to\s+
                (.*)                                        # lc_state
                /ix;

            $lc_interface_count{$device}{$lc_interface}++;
            $lc_state_count{$lc_state}++;
            $lc_device_total{$device}++;
            $lc_total++;

            next;
        }

        # port aggregation protocol from STP
        #
        # %PAGP-5-PORTFROMSTP: Port [slot/port] left bridge port [slot/port]

        if ($msg =~
            /\%PAGP-5-PORTFROMSTP\:Port\s+\d+\/\d+\s+left\s+bridge\s+port\s+\d+\/\d+/i
           ) {
            my ($papfs_port, $papfs_bridgeport) = (0, 0);

            ($papfs_port, $papfs_bridgeport) = $msg =~
                /\%PAGP-5-PORTFROMSTP\:Port\s+
                (\d+\/\d+)                                  # papfs_port
                \s+left\s+bridge\s+port\s+
                (\d+\/\d+)                                  # papfs_bridgeport
                /ix;

            $papfs_device_total{$device}++;
            $papfs_total++;

            next;
        }

        # port aggregation protocol to STP
        #
        # %PAGP-5-PORTTOSTP: Port [slot/port] joined bridge port [slot/port]

        if ($msg =~
            /\%PAGP-5-PORTTOSTP\:Port\s+\d+\/\d+\s+joined\s+bridge\s+port\s+\d+\/\d+/i
           ) {
            my ($papts_port, $papts_bridgeport) = $msg =~
                /\%PAGP-5-PORTTOSTP\:Port\s+
                (\d+\/\d+)                                 # papts_port
                \s+joined\s+bridge\s+port\s+
                (\d+\/\d+)                                 # papts_bridgeport
                /ix;

            $papts_device_total{$device}++;
            $papts_total++;

            next;
        }

        # port security reject
        #
        # %PORT_SECURITY-2-SECURITYREJECT: Security violation occurred on
        # module [mod] port [port] caused by MAC address [mac address]

        if ($msg =~
            /\%PORT_SECURITY-2-SECURITYREJECT\:\s+Security\s+violation\s+occurred\s+on\s+module\s+\d+\s+port\s+\d+\s+caused\s+by\s+MAC\s+address\s+\w{4}\.\w{4}\.\w{4}/i
           ) {
            my ($psr_mod, $psr_port, $psr_mac) = $msg =~
                /\%PORT_SECURITY-2-SECURITYREJECT\:\s+
                Security\s+violation\s+occurred\s+on\s+
                module\s+
                (\d+)                                       # psr_mod
                \s+port\s+
                (\d+)                                       # psr_port
                \s+caused\s+by\s+MAC\s+address\s+
                (\w{4}\.\w{4}\.\w{4})                       # psr_mac
                /ix;

            $psr_devmodportmac_total{"$device\t$psr_mod\/$psr_port\t$psr_mac"}++;
            $psr_device_total{$device}++;
            $psr_total++;

          next;
       }

       # multicast rx join range
       #
       # %MCAST-4-RX_JNRANGE:IGMP: Rcvd Report in the range of 01-00-5e-00-00-xx

       if ($msg =~
           /\%MCAST-4-RX_JNRANGE\:IGMP\:\s+Rcvd\s+Report\s+in\s+the\s+range\s+01-00-5e-00-00-xx/i
          ) {

           $mrjr_device_count{$device}++;
           $mrjr_total++;

           next;
       }

       # multicast rx IGMP report
       #
       # %MCAST-4-RX_IGMPREPORT:IGMP: Rcvd IGMP Report [MAC] on [mod]/[port]

       if ($msg =~
           /\%MCAST-4-RX_IGMPREPORT\:IGMP\:\s+Rcvd\s+IGMP\s+Report\s+\w{2}(-\w{2}){5}\s+on\s+\d+\/\d+/i
          ) {

           $mrir_device_count{$device}++;
           $mrir_total++;

           next;
       }

       # traffic from multicast source address
       #
       # %SYS-4-P2_WARN: 1/Invalid traffic from multicast source address
       # [MAC] on port [mod]/[port]

       if ($msg =~
           /\%SYS-4-P2_WARN\:\s+1\/Invalid\s+traffic\s+from\s+multicast\s+source\s+address\s+\w{2}(\:\w{2}){5}\s+on\s+port\s+\d+\/\d+/i
          ) {

           $tfmsa_device_count{$device}++;
           $tfmsa_total++;

           next;
       }

       # CDP native VLAN mistmatch
       #
       # %CDP-4-NATIVE-VLAN-MISMATCH: Native VLAN mismatch discovered on
       # [mod]/[port] ([vlan]), with [switch] [mod]/[port] (vlan).

       #if ($msg =~
       #    /\%CDP-4-NATIVE-VLAN-MISMATCH\:\s+Native\s+VLAN\s+mismatch\s+discovered\s+on\s+\S+\d+\/\d+\s+\(\d+\)\,\s+with\s+\S+\s+\S+\d+\/\d+\s+\(\d+\)\./i
### TMP
 
       #
       # other (uncategorized log messages) - DEFAULT CASE
       if ($msg =~ /\%\S+:/i) {
           my ($other_type) = (0);

           ($other_type) = $msg =~
               /%
               (\S+)                # other_type
               \:.*/ix;

           $other_total++;
           $other_type_count{$other_type}++;
           $other_device_total{$device}++; 

           if ($DEBUG > 1) {
               print "----------------------------------\n";
               print "[DEBUG] uncategorized log message:\n";
               print "\n$log\n";
           }

           next;
       }

       $unknown_total++;
       if ($DEBUG) {
           print "---------------------------\n";
           print "[DEBUG] unknown log message:\n";
           print "\n$log\n";
       }

   } # END SWITCH

} # END WHILE

#
# generate output
#
print "\ncislog $VERSION\n";
print "Online help: $MAN\n\n";

# if there are other/unknown log messages and DEBUG is disabled, issue warning
if ( ($other_total || $unknown_total) && !$DEBUG) {
    print "NOTICE: unknown/uncategorized logs detected.  ";
    print "Enable DEBUG to view.\n\n";
}

print "---------------\n";
print " cislog report\n";
print "---------------\n\n";

print "Total log messages: $log_count\n";
if ($tp_total) { print "traceback/process errors: $tp_total\n"; }
if ($smf_total) { print "system MALLOC failures: $smf_total\n"; }
if ($sr_total) { print "system resets/reloads: $sr_total\n"; }
if ($ch_total) { print "CPU hog alerts: $ch_total\n"; }
if ($oac_total) { print "OSPF adjancency changes: $oac_total\n"; }
if ($dlp_total) { print "device login ACL permits: $dlp_total\n"; }
if ($dld_total) { print "device login ACL denies: $dld_total\n"; }
if ($ald_total) { print "interface ACL denies: $ald_total\n"; }
if ($alp_total) { print "interface ACL permits: $alp_total\n"; }
if ($psr_total) { print "port security violations: $psr_total\n"; }
if ($rc_total) { print "RSHELL command attempts: $rc_total\n"; }
if ($dc_total) { print "device configured notifications: $dc_total\n"; }
if ($lu_total) { print "link up/down alerts: $lu_total\n"; }
if ($lpu_total) { print "line protocol up/down alerts: $lpu_total\n"; }
if ($rdlf_total) { print "runtime diag link flaps: $rdlf_total\n"; }
if ($le_total) { print "link errors: $le_total\n"; }
if ($rdaf_total) { print "runtime diag address flaps: $rdaf_total\n"; }
if ($lc_total) { print "link changes: $lc_total\n"; }
if ($papfs_total) { print "PAGP from STP alerts: $papfs_total\n"; }
if ($papts_total) { print "PAGP to STP alerts: $papts_total\n"; }
if ($mrjr_total) { print "mcast rx join range alerts: $mrjr_total\n"; }
if ($mrir_total) { print "mcast rx IGMP reports: $mrir_total\n"; }
if ($tfmsa_total) { print "traffic w/ mcast src addr: $tfmsa_total\n"; }
if ($other_total) { print "uncategorized: $other_total\n"; }
if ($unknown_total) { print "unknown log messages: $unknown_total\n"; }

# tp_device_count - traceback/process device total messages
if ($tp_total) {
    print "\nTop $TOPx devices with Traceback or Process error messages\n";
    print "$MAN#tp\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$tp_device_count{$b} -
                       $tp_device_count{$a}}
                  keys %tp_device_count) {
        print "$tp_device_count{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# smf_device_count - system malloc failure device total messages
if ($smf_total) {
    print "\nTop $TOPx devices with MALLOC failure messages\n";
    print "$MAN#smf\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$smf_device_count{$b} -
                       $smf_device_count{$a}}
                  keys %smf_device_count) {
        print "$smf_device_count{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# sr_device_total - system reload device total
if ($sr_total) {
    print "\nTop $TOPx devices being reloaded\n";
    print "$MAN#sr\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$sr_device_total{$b} -
                       $sr_device_total{$a}}
                  keys %sr_device_total) {
        print "$sr_device_total{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# ch_device_total - CPU hog process device total
if ($ch_total) {
    print "\nTop $TOPx devices with CPU hog process messages\n";
    print "$MAN#ch\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$ch_device_total{$b} -
                       $ch_device_total{$a}}
                  keys %ch_device_total) {
        print "$ch_device_total{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# ch_process_count - CPU hog process count
if ($ch_total) {
    print "\nTop $TOPx CPU hog process message types\n";
    print "$MAN#ch\n";
    print "Count\tProcess\n";
    print "-----\t-------\n";
    my $counter = 1;
    for my $item (sort{$ch_process_count{$b} -
                       $ch_process_count{$a}}
                  keys %ch_process_count) {
        print "$ch_process_count{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# oac_device_total - OSPF adjancency change device total
## TEMP: also compute neighbors, start/end state totals?
if ($oac_total) {
    print "\nTop $TOPx devices logging OSPF adjancency changes\n";
    print "$MAN#oac\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$oac_device_total{$b} -
                       $oac_device_total{$a}}
                  keys %oac_device_total) {
        print "$oac_device_total{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# other_type_count - other message type count
if ($other_total) {
    print "\nTop $TOPx other types of log messages\n";
    print "$MAN#other\n";
    print "Count\tType\n";
    print "-----\t----\n";
    my $counter = 1;
    for my $item (sort{$other_type_count{$b} -
                       $other_type_count{$a}}
                  keys %other_type_count) {
        print "$other_type_count{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# other_device_total - other messages device total
if ($other_total) {
    print "\nTop $TOPx devices logging other types of messages\n";
    print "$MAN#other\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$other_device_total{$b} -
                       $other_device_total{$a}}
                  keys %other_device_total) {
        print "$other_device_total{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# dld_src_count - device login deny source count
if ($dld_total) {
    print "\nTop $TOPx source IPs denied login to devices\n";
    print "$MAN#dld\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$dld_src_count{$b} -
                       $dld_src_count{$a}}
                  keys %dld_src_count) {
        if ($RESOLVE) {
            my $name = "";
            $name = gethostbyaddr(inet_aton($item), AF_INET)
                or $name = "[lookup failed]";
            print "$dld_src_count{$item}\t$item\t$name\n";
        }
        else {
            print "$dld_src_count{$item}\t$item\n";
        }
        last if ++$counter > $TOPx;
    }
}

# dlp_src_count - device login permit source count
if ($dlp_total) {
    print "\nTop $TOPx source IPs logging into devices\n";
    print "$MAN#dlp\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$dlp_src_count{$b} -
                       $dlp_src_count{$a}}
                  keys %dlp_src_count) {

        # check for valid IP, because some sources are vty's, tty's, etc.

        if ($RESOLVE && $item =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) {
            my $name = "";
            $name = gethostbyaddr(inet_aton($item), AF_INET)
                or $name = "[lookup failed]";
            print "$dlp_src_count{$item}\t$item\t$name\n";
        }
        else {
            print "$dlp_src_count{$item}\t$item\n";
        }
        last if ++$counter > $TOPx;
    }
}

# ald_dst_port_count - access list deny destination port count
if ($ald_total) {
    print "\nTop $TOPx destination ports being denied\n";
    print "$MAN#ald\n";
    print "Count\tPort\n";
    print "-----\t----\n";
    my $counter = 1;
    for my $item (sort{$ald_dst_port_count{$b} -
                       $ald_dst_port_count{$a}}
                  keys %ald_dst_port_count) {
        # we don't have the proto, but use tcp regardless, should still work
        if (my $port = getservbyport($item, "tcp")) {
            print "$ald_dst_port_count{$item}\t$item\t$port\n";
        }
        else {
            print "$ald_dst_port_count{$item}\t$item\t[unknown port]\n";
        } 
        last if ++$counter > $TOPx;
    }
}

# ald_acl_count - access list deny ACL count
if ($ald_total) {
    print "\nTop $TOPx ACLs logging denied packets\n";
    print "$MAN#ald\n";
    print "Count\tACL\n";
    print "-----\t---\n";
    my $counter = 1;
    for my $item (sort{$ald_acl_count{$b} -
                       $ald_acl_count{$a}}
                  keys %ald_acl_count) {
        print "$ald_acl_count{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# ald_proto_count - access list deny proto count
if ($ald_total) {
    print "\nTop $TOPx protocols being denied\n";
    print "$MAN#ald\n";
    print "Count\tProtocol\n";
    print "-----\t--------\n";
    my $counter = 1;
    for my $item (sort{$ald_proto_count{$b} -
                       $ald_proto_count{$a}}
                  keys %ald_proto_count) {
        print "$ald_proto_count{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# ald_src_count - access list deny source count
if ($ald_total) {
    print "\nTop $TOPx sources being denied\n";
    print "$MAN#ald\n";
    print "Count\tSources\n";
    print "-----\t-------\n";
    my $counter = 1;
    for my $item (sort{$ald_src_count{$b} -
                       $ald_src_count{$a}}
                  keys %ald_src_count) {
        if ($RESOLVE) {
            my $name = "";
            $name = gethostbyaddr(inet_aton($item), AF_INET)
                or $name = "[lookup failed]";
            print "$ald_src_count{$item}\t$item\t$name\n";
        }
        else {
            print "$ald_src_count{$item}\t$item\n";
        }
        last if ++$counter > $TOPx;
    }
}

# ald_icmp_count - access list deny icmp count
if (%ald_icmp_count) {
    print "\nTop $TOPx ICMP messages being denied\n";
    print "$MAN#ald\n";
    print "Count\tType/Code\n";
    print "-----\t---------\n";
    my $counter = 1;
    for my $item (sort{$ald_icmp_count{$b} -
                       $ald_icmp_count{$a}}
                  keys %ald_icmp_count) {
        my $icmp = "[unassigned]";
        SWITCH: {
            if ($item =~ /^0\/0$/) { $icmp = "echo reply"; }
            if ($item =~ /^3\/0$/) { $icmp = "net unreachable"; }
            if ($item =~ /^3\/1$/) { $icmp = "host unreachable"; }
            if ($item =~ /^3\/2$/) { $icmp = "protocol unreachable"; }
            if ($item =~ /^3\/3$/) { $icmp = "port unreachable"; }
            if ($item =~ /^3\/4$/) { $icmp = "fragmentation needed"; }
            if ($item =~ /^3\/5$/) { $icmp = "source route failed"; }
            if ($item =~ /^3\/6$/) { $icmp = "dest network unknown"; }
            if ($item =~ /^3\/7$/) { $icmp = "dest host unknown"; }
            if ($item =~ /^3\/8$/) { $icmp = "source host isolated"; }
            if ($item =~ /^3\/9$/) { $icmp = "admin prohibited - net"; }
            if ($item =~ /^3\/10$/) { $icmp = "admin prohibited - host"; }
            if ($item =~ /^3\/11$/) { $icmp = "net unreachable - ToS"; }
            if ($item =~ /^3\/12$/) { $icmp = "host unreachable - ToS"; }
            if ($item =~ /^3\/13$/) { $icmp = "admin prohibited"; } 
            if ($item =~ /^3\/14$/) { $icmp = "precedence violation"; }
            if ($item =~ /^3\/15$/) { $icmp = "precedence cutoff"; }
            if ($item =~ /^4\/0$/) { $icmp = "source quench"; }
            if ($item =~ /^5\/0$/) { $icmp = "network redirect"; }
            if ($item =~ /^5\/1$/) { $icmp = "host redirect"; }
            if ($item =~ /^5\/2$/) { $icmp = "ToS network redirect"; }
            if ($item =~ /^5\/3$/) { $icmp = "ToS host redirect"; }
            if ($item =~ /^6\/0$/) { $icmp = "alternate host address"; }
            if ($item =~ /^8\/0$/) { $icmp = "echo"; }
            if ($item =~ /^9\/0$/) { $icmp = "normal router advertisement"; }
            if ($item =~ /^9\/16$/) { $icmp = "does not route common traffic"; }
            if ($item =~ /^10\/0$/) { $icmp = "router selection"; }
            if ($item =~ /^11\/0$/) { $icmp = "TTL exceeded in transit"; }
            if ($item =~ /^11\/1$/) { $icmp = "frag reassembly TTL exceeded"; } 
            if ($item =~ /^12\/0$/) { $icmp = "pointer indicates error"; }
            if ($item =~ /^12\/1$/) { $icmp = "missing a required option"; }
            if ($item =~ /^12\/2$/) { $icmp = "bad length"; }
            if ($item =~ /^13\/0$/) { $icmp = "timestamp"; }
            if ($item =~ /^14\/0$/) { $icmp = "timestamp reply"; }
            if ($item =~ /^15\/0$/) { $icmp = "information request"; }
            if ($item =~ /^16\/0$/) { $icmp = "information reply"; }
            if ($item =~ /^17\/0$/) { $icmp = "address mask request"; }
            if ($item =~ /^18\/0$/) { $icmp = "address mask reply"; }
            if ($item =~ /^19\/\d+$/) { $icmp = "reserved for security"; }
            if ($item =~ /^2\d\/\d+$/) { $icmp = "reserved for robustness"; }
            if ($item =~ /^30\/\d+$/) { $icmp = "traceroute"; }
            if ($item =~ /^31\/\d+$/) { $icmp = "conversion error"; }
            if ($item =~ /^32\/\d+$/) { $icmp = "mobile host redirect"; }
            if ($item =~ /^33\/\d+$/) { $icmp = "IPv6 where-are-you"; }
            if ($item =~ /^34\/\d+$/) { $icmp = "IPv6 I-am-here"; }
            if ($item =~ /^35\/\d+$/) { $icmp = "mobile reg request"; }
            if ($item =~ /^36\/\d+$/) { $icmp = "mobile reg reply"; }
            if ($item =~ /^39\/\d+$/) { $icmp = "SKIP"; }
            if ($item =~ /^40\/0$/) { $icmp = "bad SPI"; }
            if ($item =~ /^40\/1$/) { $icmp = "authentication failed"; }
            if ($item =~ /^40\/2$/) { $icmp = "decompression failed"; }
            if ($item =~ /^40\/3$/) { $icmp = "decryption failed"; }
            if ($item =~ /^40\/4$/) { $icmp = "need authentication"; }
            if ($item =~ /^40\/5$/) { $icmp = "need authorization"; }
        }
        print "$ald_icmp_count{$item}\t$item\t$icmp\n";
        last if ++$counter > $TOPx;
    }
}

# ald_device_total - access list deny device total
if ($ald_total > 0) {
    print "\nTop $TOPx devices denying packets\n";
    print "$MAN#ald\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$ald_device_total{$b} -
                       $ald_device_total{$a}}
                  keys %ald_device_total) {
        print "$ald_device_total{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# alp_acl_count - access list permit ACL count
if ($alp_total > 0) {
    print "\nTop $TOPx ACLs permitting packets\n";
    print "$MAN#alp\n";
    print "Count\tACL\n";
    print "-----\t---\n";
    my $counter = 1;
    for my $item (sort{$alp_acl_count{$b} -
                       $alp_acl_count{$a}}
                  keys %alp_acl_count) {
        print "$alp_acl_count{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# alp_proto_count - access list permit proto count
if ($alp_total) {
    print "\nTop $TOPx protocols being permitted\n";
    print "$MAN#alp\n";
    print "Count\tProtocol\n";
    print "-----\t--------\n";
    my $counter = 1;
    for my $item (sort{$alp_proto_count{$b} -
                       $alp_proto_count{$a}}
                  keys %alp_proto_count) {
        print "$alp_proto_count{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# alp_src_count - access list permit source count
if ($alp_total) {
    print "\nTop $TOPx sources being permitted\n";
    print "$MAN#alp\n";
    print "Count\tSources\n";
    print "-----\t-------\n";
    my $counter = 1;
    for my $item (sort{$alp_src_count{$b} -
                       $alp_src_count{$a}}
                  keys %alp_src_count) {
        if ($RESOLVE) {
            my $name = "";
            $name = gethostbyaddr(inet_aton($item), AF_INET)
                or $name = "[lookup failed]";
            print "$alp_src_count{$item}\t$item\t$name\n";
        }
        else {
            print "$alp_src_count{$item}\t$item\n";
        }
        last if ++$counter > $TOPx;
    }
}

# alp_dst_count - access list permit destination count
if ($alp_total) {
    print "\nTop $TOPx destinations being permitted\n";
    print "$MAN#alp\n";
    print "Count\tDestination\n";
    print "-----\t-----------\n";
    my $counter = 1;
    for my $item (sort{$alp_dst_count{$b} -
                       $alp_dst_count{$a}}
                  keys %alp_dst_count) {
        if ($RESOLVE) {
            my $name = "";
            $name = gethostbyaddr(inet_aton($item), AF_INET)
                or $name = "[lookup failed]";
            print "$alp_dst_count{$item}\t$item\t$name\n";
        }
        else {
            print "$alp_dst_count{$item}\t$item\n";
        }
        last if ++$counter > $TOPx;
    }
}

# alp_dst_port_count - access list permit destination port count
if ($alp_total) {
    print "\nTop $TOPx destination ports being permitted\n";
    print "$MAN#alp\n";
    print "Count\tPort\n";
    print "-----\t----\n";
    my $counter = 1;
    for my $item (sort{$alp_dst_port_count{$b} -
                       $alp_dst_port_count{$a}}
                  keys %alp_dst_port_count) {
        # we don't have the proto, but use tcp regardless, should still work
        if (my $port = getservbyport($item, "tcp")) {
            print "$alp_dst_port_count{$item}\t$item\t$port\n";
        }
        else {
            print "$alp_dst_port_count{$item}\t$item\t[unknown port]\n";
        } 
        last if ++$counter > $TOPx;
    }
}

# alp_icmp_count - access list permit icmp count
if (%alp_icmp_count) {
    print "\nTop $TOPx ICMP messages being permitted\n";
    print "$MAN#alp\n";
    print "Count\tType/Code\n";
    print "-----\t---------\n";
    my $counter = 1;
    for my $item (sort{$alp_icmp_count{$b} -
                       $alp_icmp_count{$a}}
                  keys %alp_icmp_count) {
        my $icmp = "[unassigned]";
        SWITCH: {
            if ($item =~ /^0\/0$/) { $icmp = "echo reply"; }
            if ($item =~ /^3\/0$/) { $icmp = "net unreachable"; }
            if ($item =~ /^3\/1$/) { $icmp = "host unreachable"; }
            if ($item =~ /^3\/2$/) { $icmp = "protocol unreachable"; }
            if ($item =~ /^3\/3$/) { $icmp = "port unreachable"; }
            if ($item =~ /^3\/4$/) { $icmp = "fragmentation needed"; }
            if ($item =~ /^3\/5$/) { $icmp = "source route failed"; }
            if ($item =~ /^3\/6$/) { $icmp = "dest network unknown"; }
            if ($item =~ /^3\/7$/) { $icmp = "dest host unknown"; }
            if ($item =~ /^3\/8$/) { $icmp = "source host isolated"; }
            if ($item =~ /^3\/9$/) { $icmp = "admin prohibited - net"; }
            if ($item =~ /^3\/10$/) { $icmp = "admin prohibited - host"; }
            if ($item =~ /^3\/11$/) { $icmp = "net unreachable - ToS"; }
            if ($item =~ /^3\/12$/) { $icmp = "host unreachable - ToS"; }
            if ($item =~ /^3\/13$/) { $icmp = "admin prohibited"; } 
            if ($item =~ /^3\/14$/) { $icmp = "precedence violation"; }
            if ($item =~ /^3\/15$/) { $icmp = "precedence cutoff"; }
            if ($item =~ /^4\/0$/) { $icmp = "source quench"; }
            if ($item =~ /^5\/0$/) { $icmp = "network redirect"; }
            if ($item =~ /^5\/1$/) { $icmp = "host redirect"; }
            if ($item =~ /^5\/2$/) { $icmp = "ToS network redirect"; }
            if ($item =~ /^5\/3$/) { $icmp = "ToS host redirect"; }
            if ($item =~ /^6\/0$/) { $icmp = "alternate host address"; }
            if ($item =~ /^8\/0$/) { $icmp = "echo"; }
            if ($item =~ /^9\/0$/) { $icmp = "normal router advertisement"; }
            if ($item =~ /^9\/16$/) { $icmp = "does not route common traffic"; }
            if ($item =~ /^10\/0$/) { $icmp = "router selection"; }
            if ($item =~ /^11\/0$/) { $icmp = "TTL exceeded in transit"; }
            if ($item =~ /^11\/1$/) { $icmp = "frag reassembly TTL exceeded"; } 
            if ($item =~ /^12\/0$/) { $icmp = "pointer indicates error"; }
            if ($item =~ /^12\/1$/) { $icmp = "missing a required option"; }
            if ($item =~ /^12\/2$/) { $icmp = "bad length"; }
            if ($item =~ /^13\/0$/) { $icmp = "timestamp"; }
            if ($item =~ /^14\/0$/) { $icmp = "timestamp reply"; }
            if ($item =~ /^15\/0$/) { $icmp = "information request"; }
            if ($item =~ /^16\/0$/) { $icmp = "information reply"; }
            if ($item =~ /^17\/0$/) { $icmp = "address mask request"; }
            if ($item =~ /^18\/0$/) { $icmp = "address mask reply"; }
            if ($item =~ /^19\/\d+$/) { $icmp = "reserved for security"; }
            if ($item =~ /^2\d\/\d+$/) { $icmp = "reserved for robustness"; }
            if ($item =~ /^30\/\d+$/) { $icmp = "traceroute"; }
            if ($item =~ /^31\/\d+$/) { $icmp = "conversion error"; }
            if ($item =~ /^32\/\d+$/) { $icmp = "mobile host redirect"; }
            if ($item =~ /^33\/\d+$/) { $icmp = "IPv6 where-are-you"; }
            if ($item =~ /^34\/\d+$/) { $icmp = "IPv6 I-am-here"; }
            if ($item =~ /^35\/\d+$/) { $icmp = "mobile reg request"; }
            if ($item =~ /^36\/\d+$/) { $icmp = "mobile reg reply"; }
            if ($item =~ /^39\/\d+$/) { $icmp = "SKIP"; }
            if ($item =~ /^40\/0$/) { $icmp = "bad SPI"; }
            if ($item =~ /^40\/1$/) { $icmp = "authentication failed"; }
            if ($item =~ /^40\/2$/) { $icmp = "decompression failed"; }
            if ($item =~ /^40\/3$/) { $icmp = "decryption failed"; }
            if ($item =~ /^40\/4$/) { $icmp = "need authentication"; }
            if ($item =~ /^40\/5$/) { $icmp = "need authorization"; }
        }
        print "$alp_icmp_count{$item}\t$item\t$icmp\n";
        last if ++$counter > $TOPx;
    }
}

# alp_device_total - access list permit device total
if ($alp_total) {
    print "\nTop $TOPx devices permitting packets\n";
    print "$MAN#alp\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$alp_device_total{$b} -
                       $alp_device_total{$a}}
                  keys %alp_device_total) {
        print "$alp_device_total{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# psr_devmodportmac_total - port security reject device/mod/port/mac total
if ($psr_total) {
    print "\nTop $TOPx port security violations\n";
    print "$MAN#psr\n";
    print "Count\tViolation \(device\/mod\/port\/MAC\)\n";
    print "-----\t--------------------------------\n";
    my $counter = 1;
    for my $item (sort{$psr_devmodportmac_total{$b} -
                       $psr_devmodportmac_total{$a}}
                  keys %psr_devmodportmac_total) {
        print "$psr_devmodportmac_total{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# rc_src_count - RSHELL command attempt source count
if ($rc_total) {
    print "\nTop $TOPx RSHELL command attempt sources\n";
    print "$MAN#rc\n";
    print "Count\tSource\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$rc_src_count{$b} -
                       $rc_src_count{$a}}
                  keys %rc_src_count) {
        if ($RESOLVE) {
            my $name = "";
            $name = gethostbyaddr(inet_aton($item), AF_INET)
                or $name = "[lookup failed]";
            print "$rc_src_count{$item}\t$item\t$name\n";
        }
        else {
            print "$rc_src_count{$item}\t$item\n";
        }
        last if ++$counter > $TOPx;
    }
}

# dc_src_count - device configure source count
if ($dc_total) {
    print "\nTop $TOPx sources configuring devices\n";
    print "$MAN#dc\n";
    print "Count\tSource\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$dc_src_count{$b} -
                       $dc_src_count{$a}}
                  keys %dc_src_count) {
        if ($item =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ && $RESOLVE) {
            my $name = "";
            $name = gethostbyaddr(inet_aton($item), AF_INET)
                or $name = "[lookup failed]";
            print "$dc_src_count{$item}\t$item\t$name\n";
        }
        else { 
            print "$dc_src_count{$item}\t$item\n";
        }
        last if ++$counter > $TOPx;
    }
}

# dc_device_total - device configure device count
if ($dc_total) {
    print "\nTop $TOPx devices being configured\n";
    print "$MAN#dc\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$dc_device_total{$b} -
                       $dc_device_total{$a}}
                  keys %dc_device_total) {
        print "$dc_device_total{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# lu_device_total - link up/down device total
if ($lu_total) {
    print "\nTop $TOPx devices logging link up/down messages\n";
    print "$MAN#lu\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$lu_device_total{$b} -
                       $lu_device_total{$a}}
                  keys %lu_device_total) {
        print "$lu_device_total{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# lpu_device_total - line protocol up/down device total
if ($lpu_total) {
    print "\nTop $TOPx devices logging line protocol up/down messages\n";
    print "$MAN#lpu\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$lpu_device_total{$b} -
                       $lpu_device_total{$a}}
                  keys %lpu_device_total) {
        print "$lpu_device_total{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# rdlf_device_total - runtime diagnostic link flap device total
if ($rdlf_total) {
    print "\nTop $TOPx devices logging runtime diagnostic link flaps\n";
    print "$MAN#rdlf\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$rdlf_device_total{$b} -
                       $rdlf_device_total{$a}}
                  keys %rdlf_device_total) {
        print "$rdlf_device_total{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# rdaf_device_total - runtime diagnostic address flap  device total
if ($rdaf_total) {
    print "\nTop $TOPx devices logging runtime diagnostic address flaps\n";
    print "$MAN#rdaf\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$rdaf_device_total{$b} -
                       $rdaf_device_total{$a}}
                  keys %rdaf_device_total) {
        print "$rdaf_device_total{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# le_device_total - link error device total
if ($le_total) {
    print "\nTop $TOPx devices logging link errors\n";
    print "$MAN#le\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$le_device_total{$b} -
                       $le_device_total{$a}}
                  keys %le_device_total) {
        print "$le_device_total{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# lc_device_total - link change device total
if ($lc_total) {
    print "\nTop $TOPx devices logging link changes\n";
    print "$MAN#lc\n";
    print "Count\tDevice\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$lc_device_total{$b} -
                       $lc_device_total{$a}}
                  keys %lc_device_total) {
        print "$lc_device_total{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# mrjr_device_count - multicast rx join range total
if ($mrjr_total) {
    print "\nTop $TOPx multicast rx join range messages\n";
    print "$MAN#mrjr\n";
    print "Count\tSource\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$mrjr_device_count{$b} -
                       $mrjr_device_count{$a}}
                  keys %mrjr_device_count) {
        print "$mrjr_device_count{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# mrir_device_count - multicast rx IGMP report total
if ($mrir_total) {
    print "\nTop $TOPx multicast rx IGMP reports\n";
    print "$MAN#mrir\n";
    print "Count\tSource\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$mrir_device_count{$b} -
                       $mrir_device_count{$a}}
                  keys %mrir_device_count) {
        print "$mrir_device_count{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# tfmsa_device_count - traffic from multicast source address
if ($tfmsa_total) {
    print "\nTop $TOPx invalid multicast as source address messages\n";
    print "$MAN#tfmsa\n";
    print "Count\tSource\n";
    print "-----\t------\n";
    my $counter = 1;
    for my $item (sort{$tfmsa_device_count{$b} -
                       $tfmsa_device_count{$a}}
                  keys %tfmsa_device_count) {
        print "$tfmsa_device_count{$item}\t$item\n";
        last if ++$counter > $TOPx;
    }
}

# logs_per_hour
if ($log_count) {
    print "\nHourly log summary\n";
    print "Hour\tLogs \(count\/percent\)\n";
    my $counter = 0;
    for ($counter = 0; $counter < 24; $counter++) {
        print "$counter\t";

        # bargraph limited to 50 chars
        # helps ensure output fits on a single line w/o wrap
        #
        my $bargraph = 0;
        while ($bargraph < 50) {

            sub round {
                my($number) = shift;
                return int($number + .5 * ($number <=>0));
            }

            # stop bargraph printing if % of hourly logs < 1%
            #
            last if ($logs_per_hour[$counter] / $log_count) * 100 < 1 ||
                    ++$bargraph >

                        # percentage of logs = 100% / 2
                        #
                        round($logs_per_hour[$counter] / $log_count * 50);

            print "#";  # grow bargraph by one mark
        }

        printf "\(%d\/%.2f%%\)\n", $logs_per_hour[$counter],
             ($logs_per_hour[$counter] / $log_count) * 100;
    }
}

if ($unknown_total) { print "\nunknown logs detected, for further info see:\n";
                      print "$MAN#unknown\n"; }

if ($DEBUG) {
    my $current_dir = getcwd;
    my $DEBUG_end_time = localtime;
    print "\n[DEBUG] Script (approximate) start time: $DEBUG_start_time\n";
    print "[DEBUG] Script (approximate) end time: $DEBUG_end_time\n";
    print "[DEBUG] Total log message count: $log_count\n";
    print "[DEBUG] Name resolution: ";
        if ($RESOLVE) { print "enabled\n"; } else { print "disabled\n"; }
    print "[DEBUG] Rows per category report: $TOPx\n";
    print "[DEBUG] Working directory: $current_dir\n";
    print "[DEBUG] Command line: $0";
        if (@arguments) { print " @arguments\n"; } else { print "\n"; }
}

### EOF
