#!/usr/bin/perl -w
##########################################################################
# $Id: kernel,v 1.24 2003/12/15 18:09:23 kirk Exp $
##########################################################################

########################################################
# Kernel script for LogWatch 
# The latest version of this script can be found at:
#   http://snurk.org/projects/files/kernel 	
#
# Based on the kernel script of LogWatch 3.3 written by
#   Kirk Bauer <kirk@kaybee.org>
# with contributions by
#   Fabrizio Zeno Cornelli <zeno@filibusta.crema.unimi.it> 
#   Luuk de Boer <luuk_de_boer@pi.net>
#
# This script written by
#   James Wysynski <wysynskij@yahoo.com>
#
# Visit the LogWatch website at
#   www.logwatch.org
########################################################

use Logwatch ':ip';

$Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;
$DoLookup = $ENV{'kernel_ip_lookup'}; $DoLookup = $DoLookup; # keep -w happy
$MaxFlood = 10;
$MaxNum =0;

sub lookupService {
   my ($port, $proto, $service);
   ($port, $proto) = ($_[0], $_[1]);
   if ($service = getservbyport ($port, $proto)) {
      return($service);
   } else {
      return($port);
   }
}

sub lookupProtocol {
   my ($proto, $name);
   $proto = $_[0];
   if ($name = getprotobynumber ($proto)) {
      return($name);
   } else {
      return($proto);
   }
}

sub lookupAction {
   my ($chain, $actionType);
   $chain = $_[0];

   # choose an action type
   if ( $chain =~ /.*reject.*/i ) {
      $actionType = "Rejected";
   } elsif ( $chain =~ /.*drop.*/i ) {
      $actionType = "Dropped";
   } elsif ( $chain =~ /.*deny.*/i ) {
      $actionType = "Denied";
   } elsif ( $chain =~ /.*accept.*/i ) {
      $actionType = "Accepted";
   } else {
      $actionType = "Logged";
   }

   return $actionType;
}

# SORT COMPARISONS
sub compStr {
   return $a cmp $b; 
}

sub compNum {
   return $a <=> $b;
}

while (defined($ThisLine = <STDIN>)) {
   chomp($ThisLine);
   next if ($ThisLine eq '');

   # IPCHAINS 
   if ( ($from,$on) = ( $ThisLine =~ /^Warning: possible SYN flood from ([^ ]+) on ([^ ]+):.+ Sending cookies/ ) ) {
      $Fullfrom = LookupIP($from);
      $Fullon = LookupIP($on);
      $SYNflood{$Fullon}{$Fullfrom}++;
   } elsif ($ThisLine =~ /continuing in degraded mode/) {
      print " !! RAID ERROR !!\n$ThisLine\n";
   } elsif( ($TU,$from,$port,$on) = ( $ThisLine =~ /IP fw-in deny \w+ (\w+) ([^:]+):\d+ ([^:]+):(\d+) / ) ){
      if($MaxNum < ++$TCPscan{$TU}{$from}) {
         $MaxNum = $TCPscan{$TU}{$from}
      }
      $port=0;
   } elsif ( ($chain,$action,$if,$proto,$fromip,$toip,$toport) = ( $ThisLine =~ /^Packet log: ([^ ]+) (\w+) (\w+) PROTO=(\d+) ([\d|\.]+):\d+ ([\d|\.]+):(\d+)/ ) ) {
      $actionType = lookupAction($action); 
      $ipt{$actionType}{$if}{$fromip}{$toip}{$toport}{$proto}{"$chain,$if"}++;   
   }
   # IPTABLES
   elsif (($chain,$ifin,$ifout,$fromip,$toip,$proto,$rest,$ref) = ($ThisLine =~ /^(.*?)\s*IN=(\w*).*?OUT=(\w*).*?SRC=([\d|\.]+).*?DST=([\d|\.]+).*?PROTO=(\w+)([^\[]*)(.*)/ )) {

      # we ignore the reference to a previous packet
      $ref = "";

      # get a destination port number if there is one
      if (! ( ($toport) = ( $rest =~ /^.*?DPT=(\w+)/ ) ) ) {
         $toport = 0;
      }

      # get the action type
      $actionType = lookupAction($chain);

      # determine the dominant interface 
      if ($ifin  =~ /\w+/ && $ifout  =~ /\w+/) {
         $interface = $ifin;
      } elsif ($ifin =~ /\w+/) {
         $interface = $ifin;
         $ifout = "none"; 
      } else {
         $interface = $ifout;
         $ifin = "none";
      }

      # add the packet
      $ipt{$actionType}{$interface}{$fromip}{$toip}{$toport}{$proto}{"$chain,$ifin,$ifout"}++;   
   }   
   # Kernel Errors
   elsif ( ( $errormsg ) = ( $ThisLine =~ /(.*?[Ee]rror.{0,17})/ ) ) {
      # filter out smb open/read errors cased by insufficient permissions
      $SkipError = 0;
      $SkipError = 1 if $ThisLine =~ /smb_readpage_sync: .*open failed, error=-13/;
      $SkipError = 1 if $ThisLine =~ /smb_open: .* open failed, result=-13/;
      $SkipError = 1 if $ThisLine =~ /smb_open: .* open failed, error=-13/;
      $Errors{$errormsg}++ if ( (! $SkipError) || ($Detail > 8));
   }
   # OTHER  
   else {
      # XXX For now, going to ignore all other kernel messages as there
      # XXX are practically an infinite number and most of them are obviously
      # XXX not parsed here at this time.
      # filter out smb open/read errors cased by insufficient permissions
      $SkipError = 0;
      $SkipError = 1 if $ThisLine =~ /smb_readpage_sync: .*open failed, error=-13/;
      $SkipError = 1 if $ThisLine =~ /smb_open: .* open failed, result=-13/;
      $SkipError = 1 if $ThisLine =~ /smb_open: .* open failed, error=-13/;
      $Kernel{$ThisLine}++ if ( (! $SkipError) || ($Detail > 8)) ;
   }
}

# Kernel Errors
if (keys %Errors) {
   print "\nWARNING:  Kernel Errors Present\n";
   foreach $Thisone ( sort {$a cmp $b} keys %Errors ) {
      print "   " . $Thisone . "...:  " . $Errors{$Thisone} . " Time(s)\n";
   }
}

# IPCHAINS
if (keys %SYNflood) {
   print "\nWarning: SYN flood on:\n";
   foreach $ThisOne (sort compStr keys %SYNflood) {
      print "   " . $ThisOne . " from:\n";
      foreach $Next (sort compStr keys %{$SYNflood{$ThisOne}}) {
         print "      " . $Next . ": $SYNflood{$ThisOne}{$Next} Time(s)\n";   
      }      
   }
}

if (keys %TCPscan and $MaxNum>$MaxFlood) {
   print "\nWarning: ipfwadm scan detected on:\n";
   foreach $ThisOne (sort compStr keys %TCPscan) {
      print "   " . $ThisOne . " from:\n";
      foreach $Next (sort compStr keys %{$TCPscan{$ThisOne}}) {
         $TCPscan{$ThisOne}{$Next}>$MaxFlood &&
            print "      " . LookupIP($Next). ": $TCPscan{$ThisOne}{$Next} Time(s)\n";
      }
   }       
}

# IPCHAINS / IPTABLES
if (keys %ipt) {
   foreach $actionType (sort compStr keys %ipt) {
      foreach $interface (sort compStr keys %{$ipt{$actionType}}) {
         $outputMain = '';
         $interfaceCount = 0;
         foreach $fromip (sort SortIP keys %{$ipt{$actionType}{$interface}}) {
            $outputSection = '';
            $fromHostCount = 0;
            $fromHost = LookupIP($fromip);
            if ( $fromHost eq $fromip ) {
               $from = $fromHost;
            } else {
               $from = "$fromHost \($fromip\)";
            }
            undef %port_list;
            foreach $toip (sort SortIP keys %{$ipt{$actionType}{$interface}{$fromip}}) {
               $toHostCount = 0;
               $toHost = LookupIP($toip);
               if ( $toHost eq $toip ) {
                  $to = $toHost;
               } else {
                  $to = "$toHost \($toip\)";
               }
               $outputServices = '';
               foreach $toport (sort compNum keys %{$ipt{$actionType}{$interface}{$fromip}{$toip}}) {
                  foreach $proto (sort compStr keys %{$ipt{$actionType}{$interface}{$fromip}{$toip}{$toport}}) {				     
                     # determine the protocol
                     if ( $proto =~ /\d+/ ) {
                        $protocol = lookupProtocol($proto);
                     } else {
                        $protocol = lc($proto);
                     } 

                     # determine the name of the service
                     $service = lookupService($toport,$protocol);

                     foreach $details (sort keys %{$ipt{$actionType}{$interface}{$fromip}{$toip}{$toport}{$proto}}) {
                        $packetCount = $ipt{$actionType}{$interface}{$fromip}{$toip}{$toport}{$proto}{$details};
                        $toHostCount += $packetCount;
                        if ( $Detail > 0 ) {
                           $outputServices .= "         Service: $service ($protocol/$toport) ($details) - $packetCount " . ( ( $packetCount > 1 ) ? "packets\n" : "packet\n" );
                        } else {
                           push @{ $port_list{ $protocol } }, $toport;
                        }
                     }
                  }
               }
               $fromHostCount += $toHostCount;
               if ( $Detail > 0 ) { $outputSection .= "      To $to - $toHostCount " . ( ( $toHostCount > 1 ) ? "packets\n" : "packet\n" ); }
               $outputSection .= $outputServices;
            }
            $interfaceCount += $fromHostCount;
            if ($Detail > 0 ) {
               $outputMain .= "   From $from - $fromHostCount " . ( ( $fromHostCount > 1 ) ? "packets\n" : "packet\n" );
            } else {
               $outputMain .= "  From $from - $fromHostCount " .  ( ($fromHostCount > 1) ? "packets" : "packet" ) .  " to " ;
               foreach $protocol ( keys %port_list ) {
                  if ( $#{ $port_list{ $protocol } } > 10 ) {
                     $outputMain .= $#{ $port_list{ $protocol } } ." $protocol ports";
                  } else {
                     $outputMain .= "$protocol(" . join(",", @{ $port_list{ $protocol } } ) . ")" ;
                  }
               }
               $outputMain .="\n";
            }
            $outputMain .= $outputSection;
         }
         print "\n$actionType $interfaceCount " . ( ( $interfaceCount > 1 ) ? "packets" : "packet" ) . " on interface $interface\n"; 
         print $outputMain;
      }
   }
}

# OTHER
if ( ($Detail >= 5) and (keys %Kernel) ) {
   print "\n";
   foreach $ThisOne (sort {$a cmp $b} keys %Kernel) {
      print $Kernel{$ThisOne} . " Time(s): " . $ThisOne . "\n";
   }
}

exit(0);

# vi: shiftwidth=3 tabstop=3 et

