#!/usr/local/bin/perl -w
my $rcsid = '$Id: iptables.pl,v 1.4 2002/01/28 14:10:10 marceld Exp $';

# ###########################################################################################
#
# Author: Marcel Dorenbos (based on ideas from Sjaak Schilperoort, donot know where he is now?)
# email:  M.Dorenbos@concert.net

# This script parses the output from IPtables through syslog and beautify the output. Two environment
# variables are important to set, to make this work:

# $LOCALADDRESS: the IP address  of the firewall (if you have more, than it is possible to do this as well, e.g.:
#                $LOCALADDRESS = "192.168.1.1|102.168.2.1"
#                These addresses, and 127.0.0.1, are used for coloring the date time stamp if an event is
#                related to the firewall itself: green, if an accept is seen, red on a drop or reject.
# $file:         this is the file receiving the syslog information from IPtables.

# The script will notice file rotation on $file, it will reopen the new file.
# Also, most lines will probably be not interesting and clutter the screen. It is possible to add/extend regular
# expression in subroutine kill to filter out these events. They will be in the syslog file, but not shown on
# the screen. The example below will filter out LANmanager 'broadcasts' and tftp broadcasts.
# MAC addresses are filtered out of the output as well.

# Every 25 lines from syslog a status message is sent to the screen, displaying howmany lines have been 
# killed (discarded).

# For this script you need to have Term::ANSIColor, which can be found at CPAN.

use Term::ANSIColor qw(:constants);

my $LOCALADDRESS = '192.168.117.1';
my $file = "/var/log/iptables";
my $HOST = `hostname`;
print $HOST;

my $CLEAR = CLEAR; my $RESET = RESET;
my $BOLD = BOLD;
my $UNDERLINE = UNDERLINE; my $UNDERSCORE = UNDERSCORE; my $BLINK = BLINK;
                                 
my $REVERSE = REVERSE;
my $CONCEALED = CONCEALED;
my $BLACK = BLACK; my $RED = RED; my $GREEN = GREEN; my $YELLOW = YELLOW;
my $BLUE = BLUE; my $MAGENTA = MAGENTA; my $CYAN = CYAN; my $WHITE = WHITE;
my $ON_BLACK = ON_BLACK; my $ON_RED = ON_RED;
                                 
my $ON_GREEN = ON_GREEN; my $ON_YELLOW = ON_YELLOW; my $ON_BLUE = ON_BLUE; my $ON_MAGENTA = ON_MAGENTA;
my $ON_CYAN = ON_CYAN; my $ON_WHITE = ON_WHITE;

$debug = 0;
$timeout = 5;
$alarm = 600;
$k = $s = $t = 0;
$SIG{'INT'} = 'stats';
$SIG{'ALRM'} = 'wakeup';
alarm($alarm);

sub wakeup {
    $i = (stat($file))[1];
    if ($i > 0 && $i != $ino) {
	$ino = $i;
        $inochanged = 1;
    }
    alarm($alarm);
}

&wakeup;			# read inode and opens file

while (1) {

    if ($inochanged) {
       &debug("inode ", $ino ? "changed" : "set", " to $i");
       print "inode has changed\n";
       $inochanged = 0;

       close(LOG);

       open(LOG, "<$file");
       seek(LOG, -10000, 2);
       $dummy=<LOG>;

       $t = $k = $s = 0; # reset values as well!
    }

    $_= <LOG>;
    $nread = length($_);
    &debug("read $nread chars");
    &display;

    if ( (int($t / 25) * 25) == $t) {
       ($sec, $min, $hour, $mday, $mon, $year, $wday, $ydat, $isdst) = localtime();
       $now = sprintf("M%02d%02d %02d\:%02d\:%02d", 1900 + $year, ++$mon, $mday, $hour, $min, $sec);

       printf("$now Total: $t  Killed: $k (%3.2f%%)\n", 100 * $k / $t);
    }

    while (eof) {
	&debug("EOF");
	sleep $timeout;
   }
}

sub debug {
    print "(debug) @_\n" if $debug;
}

sub display {
	($t++, $k++, return) if &kill ;
	chop($_);	# chop  off newline

	s/ws-5085\s//;
	s/(MAC=\S+) //;
 	s/IN=\s//;
 	s/OUT=\s//;
	s/Deny /$RED${BOLD}Deny $RESET/;
	s/Accept /$GREEN${BOLD}Accept $RESET/;

        if ( /(Accept|Deny).*$LOCALADDRESS|127.0.0.1/ ) {
           my $color = "$YELLOW$BOLD$ON_RED";
           $color = "$BOLD$BLUE$ON_GREEN" if $1 eq "Accept";
           my $line = $_;
           substr($line, 15, 0) = "$RESET";
           $_ =  "$color$line";
           
        }

	/RULE (\d+)/;
        my $rulenr = sprintf("%2.2d",$1);
        s/RULE \d+/${UNDERLINE}RULE $rulenr$RESET/;
 
	s/(SRC=\S+|SPT=\S+)/$BLUE$BOLD$1$RESET/g;
	s/(DST=\S+|DPT=\S+)/$BLUE$UNDERLINE$1$RESET/g;
	s/(PROTO=\S+)/$BLUE$1$RESET/;

	print $_ . "\n";
	$t++;
 }

sub kill {
	/\d+\.\d+\.\d+\.255.*SPT\=13[7-9] DPT\=13[7-9]/ ||
	/255.255.255.255.*DPT\=69/ 
}

sub stats {
	if ($t) {
		printf("Total: $t\n");
		printf("Killed: $k (%d%%)\n", 100 * $k / $t);
		printf("Selected: $s (%d%%)\n", 100 * $s / $t);
	}
	exit;
}

