#!/usr/bin/perl -w # firewall-log.pl by Nick Craig-Wood use strict; use Socket; # User configurable constants my $LOG = "/var/log/messages"; my $TAIL = "tail -n 1000 -fname $LOG --retry"; ############################################################ my $USAGE = << "EOF"; This looks through the system log and translates any messages from the firewall into coloured human readable format, rather than a whole string of numbers intermixed with a lot of non-firewall information. It can also look at the output of dmesg which is useful if your klogd blows up or you want to look at it not as root. Useful if you run Linux + an ipchains based firewall. Syntax: $0 [-t] [file] Use it like this :- $0 $LOG Or dmesg | $0 Or tail -f $LOG | $0 Or $TAIL | $0 Since the last of these is so useful (open a window and tail the firewall attempts for ever), it can be run like this :- $0 -t EOF ############################################################ # Process the arguments if (@ARGV > 0 && $ARGV[0] =~ /^-/) { my $switch = shift; if ($switch eq '-t') { unshift @ARGV, "$TAIL |"; } else { die $USAGE; } } # use ANSI colours if we are attached to a terminal - could use # Term::ANSIColors here but I decided not to to make the script as # portable as possible. my ( $RED, $GREEN, $BLUE, $NORMAL ) = -t STDOUT ? ( "\033[31m", "\033[32m", "\033[34m", "\033[0m" ) : ( "", "", "", "" ) ; # Grep all the relevant lines out of the incoming stream while (<>) { chomp; # We need to match in dmesg or syslog format. Syslog is the same # as dmesg with a few extra fields on the front # # Dmesg format :- # Packet log: input DENY eth1 PROTO=1 1.2.3.4:8 255.255.255.255:0 L=92 S=0x00 I=1936 F=0x0000 T=245 (#5) # # Syslog format :- # Oct 5 11:58:42 yourmachine kernel: Packet log: ... if (my @arg = /^(?:(.*) (\S+) kernel: )?Packet log: (\S+) (\S+) (\S+) PROTO=(\S+) ([0-9.]+):(\d+) ([0-9.]+):(\d+) L=(\S+) S=(\S+) I=(\S+) F=(\S+) T=(\S+)(.*)( \(.*\))? .*$/) { my ($when, $who, $chain, $status, $interface, $protocol, $source_ip, $source_port, $dest_ip, $dest_port, $length, $service, $id, $fragment, $ttl, $special, $count) = @arg; $when ||= "?"; # these aren't defined in the dmesg format log $who ||= "?"; # Prettify some inscrutable numbers $protocol = pretty_protocol($protocol); $source_ip = pretty_ip($source_ip); $dest_ip = pretty_ip($dest_ip); $source_port = pretty_port($source_port, $protocol); $dest_port = pretty_port($dest_port, $protocol); # Colour some important bits my $colour = $NORMAL; $colour = $RED if $status eq "REJECT"; $colour = $RED if $status eq "DENY"; $colour = $GREEN if $status eq "ACCEPT"; $special =~ s/SYN/${BLUE}SYN${NORMAL}/; # Report what we have found print "$when $chain $colour$status$NORMAL $interface $protocol $source_ip $source_port $dest_ip $dest_port $special\n"; #print " length = $length, service = $service, id = $id, fragment = $fragment, ttl = $ttl, remainder= $remainder\n"; } } exit; ############################################################ # Prettify a raw IP address # # Look up the name attached to the IP address and return that with # the IP address in brackets if possible. Cache the values returned. ############################################################ my %reverse_dns = (); # ip address cache sub pretty_ip { my ($ip) = @_; if (!defined $reverse_dns{$ip}) { my ($name) = gethostbyaddr(inet_aton($ip), AF_INET); $reverse_dns{$ip} = $name ? "$name [$ip]" : $ip; } return $reverse_dns{$ip} } ############################################################ # Prettify a raw port # # Look up the name attached to the port/protocol pair and return that # with the port in brackets if possible. Cache the values returned. ############################################################ my %reverse_port = (); # port cache sub pretty_port { my ($port, $proto) = @_; if (!defined $reverse_port{$proto}{$port}) { my ($name) = getservbyport($port, $proto); $reverse_port{$proto}{$port} = $name ? "$name [$port]" : $port;; } return $reverse_port{$proto}{$port} } ############################################################ # Prettify a raw protocol # # Look up the name attached to the protocol and return that name or # the number if not found. Cache the values returned ############################################################ my %reverse_protocol = (); # protocol cache sub pretty_protocol { my ($protocol) = @_; if (!defined $reverse_protocol{$protocol}) { $reverse_protocol{$protocol} = getprotobynumber($protocol) || $protocol; } return $reverse_protocol{$protocol} }