#!/usr/bin/perl -w
##########################################################################
# $Id: kernel,v 1.14 2002/10/24 19:19:12 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
########################################################

$Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'};
$DoLookup = $ENV{'kernel_ip_lookup'};
$MaxFlood = 10;
$MaxNum =0;

# LOOKUP FUNCTIONS
sub lookupIP {
  my ($name, $a1,$a2,$a3,$a4,$PackedAddr,$Addr);
  if ($DoLookup) {
    $Addr = $_[0];
    ($a1,$a2,$a3,$a4) = split /\./,$Addr;
    $PackedAddr = pack('C4',$a1,$a2,$a3,$a4);
    if ($name = gethostbyaddr ($PackedAddr,2)) {
      return($name);
    } else {
      return('unknown');
    }
  } else {
    return($_[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;
}

sub compIP {
  my ($a1,$a2,$a3,$a4,$aval,$bval);
  
  # get numeric values for a and b
  ($a1,$a2,$a3,$a4) = split /\./,$a;   
  $aval = ($a1 << 24) | ($a2 << 16) | ($a3 << 8) | $a4;
  ($a1,$a2,$a3,$a4) = split /\./,$b;   
  $bval = ($a1 << 24) | ($a2 << 16) | ($a3 << 8) | $a4;
  
  return $aval <=> $bval;
}

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( ($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+) (\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"}++;   
  }   
  # 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.
    $Kernel{$ThisLine}++;     
  }
}

# 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}}) {
      print "\n$actionType packets on interface $interface:\n";
      $interfaceCount = 0;
      foreach $fromip (sort compIP keys %{$ipt{$actionType}{$interface}}) {
	$fromHostCount = 0;
	$fromHost = lookupIP($fromip);
	print "\n   From $fromHost \($fromip\).\n";
	foreach $toip (sort compIP keys %{$ipt{$actionType}{$interface}{$fromip}}) {
	  $toHostCount = 0;
	  $toHost = lookupIP($toip);
	  print "      To $toHost \($toip\).\n";
	  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);
	      print"         Service: $service ($protocol/$toport)\n";
	      
	      foreach $details (sort keys %{$ipt{$actionType}{$interface}{$fromip}{$toip}{$toport}{$proto}}) { 		 
		$packetCount = $ipt{$actionType}{$interface}{$fromip}{$toip}{$toport}{$proto}{$details};
		$toHostCount += $packetCount;
		print "            \($details\) - $packetCount packet(s)\n";
	      }
	    }
	  }
	  $fromHostCount += $toHostCount;
	  print "      Total of $toHostCount packet(s).\n";
	}
	$interfaceCount += $fromHostCount;
	print "   Total of $fromHostCount packet(s).\n";
      }
      print "\nTotal of $interfaceCount packet(s).\n"; 
    }
  }
}

# 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);

