#!/usr/bin/perl

# 
# IPF log file scanner and report generator.
# By mcr@sandelman.ca.
# See http://www.sandelman.ca/SSW/ipfreport/
#
# Requires some editing below, feed logs into it each night,
# e.g.
#   zcat /var/log/ipf.0.gz | ipfreport
#  
#
# $Id: ipfreport.pl,v 1.1 2002/05/24 02:08:47 mcr Exp $
#
# $Log: ipfreport.pl,v $
# Revision 1.1  2002/05/24 02:08:47  mcr
# 	added missing files.
#
# Revision 1.4  2002/05/22 15:05:03  mcr
# kill dest entries and ignore .0 destinations.
#
# Revision 1.3  2002/05/05 21:12:53  mcr
# added some traceroute and portscan detection.
# ignore pnap.net twits.
#
# Revision 1.2  2002/05/05 18:03:39  mcr
# added reverse name lookup.
#
# Revision 1.2  2001/05/14 13:33:58  mcr
# added origin profile
#
# Revision 1.1  2001/05/14 13:09:53  mcr
# Initial revision
#
#
# May 13 20:02:41 bud ipmon[28]: 20:02:40.472719              de0 @0:1 L 209.202.58.201,3711 -> 209.151.24.2,3128 PR tcp len 20 12288 -S IN  

# 
# May  5 07:14:17 bud ipmon[201]: 07:14:16.472817 6x              de0 @0 :1 L 66.140.139.57,62210 -> 192.139.46.0,38293 PR udp len 20 11264  IN 
#

use Socket;
use Carp;

# assume no header
$header=0;

# CONFIG me here
for $ip (0..255) {
  $myip{"192.139.46.$ip"}=1;
}
$myip{"216.187.106.126"}=1;


while(<>) {
  chop;

  $full=$_;

  next unless (/(.*)\s*(de\d+|ex\d+|tlp\d+)\s*(.*)/);

  $initial=$1;
  $if=$2;
  $violation=$3;

  ($month, $day, $time, $host, $process, $time) = split(/\s+/, $initial);
  ($ruleno, $action, $source, $arrow, $dest, $pr, $protocol, $lenstr, $length, $foo, $syn, $dir) = split(/\s+/, $violation);

  if($header == 0) {
    if($month =~ /From / || $month =~ /:/) {
      # eat message header
      while(<>) {
	chop;
	#print "dropping: $_\n";
	last if(/^$/);
      }
      ($month, $day, $time, $host, $process, $time, $if, $ruleno, $action, $source,
       $arrow, $dest, $pr, $protocol, $lenstr, $length, $foo, $syn, $dir) = split;
    }
  }

  next unless ($process =~ /ipmon/);

  if($source eq "->" || $dest eq "->" ) {
    print "BAD line: $full\n";
    next;
  }

  if($source =~ m/(.*),(.*)/) {
    $srcaddr = $1;
    $srcport = $2;
  } else {
    $srcaddr = $source;
    $srcport = "IP";
  }

  if($dest =~ m/(.*),(.*)/) {
    $dstaddr = $1;
    $dstport = $2;
  } else {
    $dstaddr = $dest;
    $dstport = "IP";
  }

  #  print "source: $source for $_\n";

  # okay, now we are interested in:
  #     $srcaddr, $dstaddr, $srcport, $dstport, $dir, $syn

  $attack{$source."+".$dest}++;

  $baddst{$dest}++;
  if($baddst{$dest} > 20) {
    $lastbaddst{$dest}=$full;
  }
  $baddstaddr{$dstaddr}++;
  $baddstport{$dstport}++;

  if(!defined($badsrcdst{$srcaddr})) {
    $badsrcdst{$srcaddr}=[];
  }
  push @{ $badsrcdst{$srcaddr} }, $dest;

  $badsrc{$src}++;
  $badsrcaddr{$srcaddr}++;
  $badsrcport{$srcport}++;

  if($dstaddr eq "192.139.46.0") {
    #print "MYIP: ".$myip{$dstaddr}."\n";
  }

  if($myip{$dstaddr}) {
    # note that this source has hit this port X many times.
    $badsrcdstport{"$srcaddr.$destport"}++;

    $scandest{$srcaddr}{$dstport}{$dstaddr}++;
  } else {
    #print "DEST is $dstaddr of $dest\n";
  }
}

sub baddstsort {
  $baddst{$b} <=> $baddst{$a}
}

sub badsrcaddrsort {
  $badsrcaddr{$b} <=> $badsrcaddr{$a}
}

sub hostname {
	local($ipstr)=@_;


	if(defined($hostnames{$ipstr})) {
		return $hostnames{$ipstr};
	} 

	$ip=inet_aton($ipstr);
	my $name = gethostbyaddr($ip,AF_INET);

	#print "$ipstr -> $name\n";

	$hostnames{$ipstr}=$name;
	return $name;
}

sub killinfobysrc {
  local($srcip)=@_;

  for $dest ($badsrcdst{$srcip}) {
    $baddst{$dest}=undef;
  }
}

# do a report on people who scanned us

print "People who port scanned us:\n";
foreach $srcscan (sort keys %scandest) {
  foreach $portscan (keys %{ $scandest{$srcscan} } ) {

    if($portscan > 33000 && $portscan < 34000) {
      # probably is traceroute, ignore it.
      $traceroute{$srcscan}++;

      # kill dest entries
      &killinfobysrc($srcscan);

      # kill src entries from this host
      $badsrcaddr{$srcscan}=undef;

      next;
    }

    $hit=0;
    for $ip (keys %myip) {
      if ($scandest{$srcscan}{$portscan}{$ip}) {
	$hit++;
      }
    }
    
    #print "Hit for $srcscan/$portscan is $hit\n";
    if($hit > 4) {
      $host=&hostname($srcscan);
      print "    ".sprintf("%-15s %-40s ",$srcscan,$host)." scanned port $portscan\n";

      # kill dest entries
      &killinfobysrc($srcscan);

      # kill src entries from this host
      $badsrcaddr{$srcscan}=undef;
    }
  }
}

print "People who have tracerouted us:\n";
# do a report on traceroute people!
@tracekeys=(keys %traceroute);
@tracekeys = sort @tracekeys;
foreach $tracer (@tracekeys) {
  $host=&hostname($tracer);

  # skip stupid twits from pnap.net.
  if($host =~ /pnap.net$/) {
    $pnap++;
    &killinfobysrc($srcip);
    next;
  }

  print "    ".sprintf("%-15s %-40s ",$tracer,$host)."\n";

  # kill dest entries
  &killinfobysrc($tracer);

  # kill src entries from this host
  $badsrcaddr{$tracer}=undef;
}

print "Please $pnap traces from pnap.net\n";


# do a report on bad origins
@srckeys=(keys %badsrcaddr);
@srckeys = sort badsrcaddrsort @srckeys;

# redo this with format, but don't have good example handy.
print "Bad origin addresses\n";
foreach $srcip (@srckeys) {
  $host=&hostname($srcip);

  print "    ".sprintf("%-15s %-40s ",$srcip,$host).$badsrcaddr{$srcip}."\n";
}

# do a report on $baddst distribution, top to bottom
@dstkeys=(keys %baddst);

@dstkeys = sort baddstsort @dstkeys;

# redo this with format, but don't have good example handy.
print "Bad destinations\n";
foreach $dst (@dstkeys) {
  ($dstip,$dstport)=split(/,/,$dst,2);

  print "   ".sprintf("%-20s %-40s ",$dst,&hostname($dstip))." ".$baddst{$dst}."\n";
  if(defined($lastbaddst{$dst})) {
    print "        ".$lastbaddst{$dst}."\n";
  }
}



  
  
