#!/usr/bin/perl
#	conntrack 0.02	Mon iptables connection tracking to stdout or tty
#
#	Copyright (C) 2002:	Manel Marin <manel3@wanadoo.es>
#	Licence:		GNU GPL version >= 2
#
#	Use:	conntrack
#		conntrack tty9
#

# Doing cat /proc/net/ip_conntrack helps little to identify connections
# This script show them in a clearer way sorting the entries by timeout


$[ = 1;		# Set array base to 1 (so $p[1] = $1 in awk)
$, = ' ';	# Set output field separator
$\ = "\n";	# Set output record separador

# FILE
$log="/proc/net/ip_conntrack";

# ASCII BEEP
$beep="\a";


# READ ARGS (array @ARGV) 
if( @ARGV ) {
    $tty = "$ARGV[1]";
    $tty =~ s|.*tty|/dev/tty|;	# Complet tty9 to /dev/tty9
}
else { $tty = ""; }

# REDIR STDOUT TO TTY IF NEEDED
if ( $tty ne "" ) {
    print "Output to $tty";
# "+<" = do not create a new file if it does not exist...
    open(STDOUT, "+< $tty") || die("Can not open tty: $!");
}


while ( 1 ) {
    # SET VARS
    $ESTABLISHEDC = $ASSUREDC = $UNREPLIEDC = 0;

    # OPEN LOG FILE
    open(LOG, "< $log") ||
	die("Can not open $log file: $!");


    $n = 0;
    while ( <LOG> ) {	# Loop for every line ($_)
	$n = $n + 1;	# Line counter
	chomp;		# Avoid last \n

# SAMPLE OF LINES
#proto   num sec (STATE) src dst sport dport ([UNREPLIED]) src dst sport dport ([ASSURED) use=1
#tcp      6 431999 ESTABLISHED src=192.168.0.2 dst=192.168.0.1 sport=1522 dport=8080 src=192.168.0.1 dst=192.168.0.2 sport=8080 dport=1522 [ASSURED] use=1
#tcp      6 110 TIME_WAIT src=192.168.0.2 dst=192.168.0.1 sport=1527 dport=8080	src=192.168.0.1 dst=192.168.0.2 sport=8080 dport=1527 [ASSURED] use=1
#udp      17 1 src=192.168.0.1 dst=192.168.0.255 sport=138 dport=138 [UNREPLIED] src=192.168.0.255 dst=192.168.0.1 sport=138 dport=138 use=1
#udp      17 169 src=127.0.0.1 dst=127.0.0.1 sport=1036 dport=53 src=127.0.0.1 dst=127.0.0.1 sport=53 dport=1036 [ASSURED] use=1
#icmp     1 29 src=1.1.1.1 dst=1.1.1.1 type=8 code=0 id=26121 [UNREPLIED] src=1.1.1.1 dst=1.1.1.1 type=0 code=0 id=26121 use=1
    # GET DATA
    # NOTE: (.*?) does a minimal match
    # TCP & UDP
    #########proto  protonum sec  {state}
	if( /^(\w+) +(\d+) +(\d+) +(.*?) *src=(.*?) +dst=(.*?) +sport=(.*?) +dport=(.*?) +(.*?) *src=.*? dst=.*? sport=.*? dport=.*? (.*)/ ){
	    $proto = $1;
	    $seconds = $3;
	    $state = $4;
	    $src = $5;
	    $dst = $6;
	    $sport = $7;
	    $dport = $8;
	    $st2 = $9;
	    $st3 = $10;
	 # PROCESS
	    $sec = $seconds;
	    if( $state eq "ESTABLISHED" ) { $sec = "-"; $ESTABLISHEDC++; }
	    $dir = "<>";
	    if( $st2 eq "[UNREPLIED]" ) { $dir = ">"; $UNREPLIEDC++; }
	    if( $st3 =~ /[ASSURED]/ ) { $ASSUREDC++; }
#	    $state = lc $state;		# to lowercase
#	    print "$proto $src:$sport $dir $dst:$dport $sec $state $st2 $st3";
	 # FORMAT OUTPUT TRYING TO FIT IN 80 CHARS
	    $src = "$src:$sport";
	    $dst = "$dst:$dport";
	    if( $state ne "" ) { $state = "$state "; }
	    if( $st2 ne "" ) { $st2 = "$st2 "; }
	    $state = "$state$st2$st3";
	 # STORE
	    $timeout = sprintf "%06d", $seconds;
	    $LINE{"$timeout$src$n"} = sprintf "%-4s %19s %2s %-19s %s %s",
		$proto, $src, $dir, $dst, $sec, $state; 
	}
    #ICMP
	elsif( /^(\w+) +(\d+) +(\d+) +(.*?) *src=(.*?) +dst=(.*?) +type=(.*?) +code=(.*?) +id=.*? +(.*?) *src=.*? dst=.*? type=.*? code=.*? id=.*? (.*)/ ){
	    $proto = $1;
	    $seconds = $3;
	    $state = $4;
	    $src = $5;
	    $dst = $6;
	    $type = $7;
	    $code = $8;
	    $st2 = $9;
	    $st3 = $10;
	 # PROCESS
	    $sec = $seconds;
	    if( $state eq "ESTABLISHED" ) { $sec = "-"; $ESTABLISHEDC++; }
	    $dir = "<>";
	    if( $st2 eq "[UNREPLIED]" ) { $dir = ">"; $UNREPLIEDC++; }
	    if( $st3 =~ /[ASSURED]/ ) { $ASSUREDC++; }
#	    $state = lc $state;		# to lowercase
#	    print "$proto $src:$type.$code $dir $dst     $sec $state $st2 $st3";
	 # FORMAT OUTPUT TRYING TO FIT IN 80 CHARS
	    $src = "$src:$type.$code";
	    if( $state ne "" ) { $state = "$state "; }
	    if( $st2 ne "" ) { $st2 = "$st2 "; }
	    $state = "$state$st2$st3";
	 # STORE
	    $timeout = sprintf "%06d", $seconds;
	    $LINE{"$timeout$src$n"} = sprintf "%-4s %19s %2s %-19s %s %s",
		$proto, $src, $dir, $dst, $sec, $state; 
	}
    # UNKNOWN
	else{
	    print "------ FORMAT UNKNOWN ------";
	    print;
	    print "------ $beep";	
	}
    }

    close(LOG);

 # MESSAGE
    print "";
    print "------ $log ------------";
 # ORDER LINES BY SECONDS AND PRINT (MORE SECONDS LAST)
    foreach $index ( sort ( keys %LINE ) ) {
	print "$LINE{$index}";
    }
    print " established=$ESTABLISHEDC assured=$ASSUREDC unreplied=$UNREPLIEDC";
    sleep 2;

 # EMPTY VAR
    %LINE = ();
}

close(STDOUT);
print "Bye...";
exit;
