#!/usr/local/bin/perl #---------------------------------------------------------------------------- # # fwreport.pl - FireWall-1 bandwidth utilization report generator # # # # Copyright (c) 1999, Jason R. Rhoads, All rights reserved. # # This software may be freely copied, modified and redistributed without # fee for non-commercial purposes provided that this copyright notice is # preserved intact on all copies. # # There is no warranty or other guarantee of fitness of this software. It # is provided solely "as is". The author disclaims all responsibility and # liability with respect to this software's usage or its effect upon # hardware or computer systems. # # Platform: Solaris 7 # Perl version: 5.004_04 # FireWall-1: Version 3.0b (Build Number: 3045) # # Notes: # This script was designed to be used in conjunction with syscheck.pl # # Bandwidth is read from the accounting log files. This information is # only generated for rules with a Track type of Account. # # Arguments: # The script takes one argument, the number of days to run the report for. # A value of 1 (the default) will run a report from yesterdays log file. # A value of 3 will run a report using data from the past 3 days, and so on. # # Installation: # Add a line similar to the following to the root crontab: # 0 1 * * * //fwreport.pl # # Revision History: # 20-May-1999 1.5 JRR Added VPN user listing # 31-Mar-1999 1.4 JRR Added spoof listing # 26-Mar-1999 1.3 JRR Added adaptive header scanning # 25-Feb-1999 1.2 JRR Added option to not report drops # 25-Jan-1999 1.1 JRR Added traceroute rollup # 18-Jan-1999 1.0 JRR Script completed # #---------------------------------------------------------------------------- # EMAIL_CMD : Path to sendmail program # FW1_DIR : Path to the FireWall-1 log directory # FW1_LOG : Prefix for FireWall-1 log files # FW1_ACC : Prefix for FireWall-1 accounting files # ZCAT_CMD : Path to compression program # # EMAIL_ADDRESS : Address to email report to # LOCAL_NET : Internal network number # DROP_MARGIN : Dropped connections that occur below this value for # a source/destination pair will not be displayed # DROP_DAYS : The maximum number of days to report drops for # SHOW_ICMP : Display icmp stats in the report (0=no, !0=yes) # TITLE : Report title # TR_ROLLUP : Combine traceroute data into single line # $EMAIL_CMD = "/usr/bin/mail -t"; $FW1_DIR = "/etc/fw/log"; $FW1_LOG = "fw1-log"; $FW1_ACC = "fw1-acc"; $ZCAT_CMD = "/usr/local/bin/gunzip -c"; $EMAIL_ADDRESS = "root"; $LOCAL_NET = "10\.0\."; $DROP_MARGIN = 2; $DROP_DAYS = 3; $SHOW_ICMP = 0; $TR_ROLLUP = 1; $TITLE = "Firewall Utilization Report"; $GBYTE = 1073741824; $FW_TYPE = 4; $FW_ACTION = 5; $FW_PROTO = 9; $FW_SRC = 10; $FW_DST = 11; $FW_SERVICE = 12; #$FW_BYTES = 17; # v3.x $FW_BYTES = 18; # v4.x $FW_USER = 21; $DAYS = $ARGV[0]; $SPOOF = "violated unidirectional connection"; $LINE = "==========================================================="; # # main # { my($hostname) = `hostname`; chop($hostname); my($i) = 1; if ($DAYS < 1) { $DAYS = 1; } while ($i <= $DAYS) { $TIMESTAMP = get_timestamp($i++); read_log_file($FW1_LOG, "log"); read_log_file($FW1_ACC, "acc"); } $REPORT .= "Firewall : $hostname\n"; $REPORT .= "Date Generated : " . get_timestamp(0) . "\n"; $REPORT .= "Report Period : "; if ($DAYS > 1) { $REPORT .= "$TIMESTAMP - " . get_timestamp(1) . " ($DAYS days)"; } else { $REPORT .= get_timestamp(1); } $REPORT .= "\n"; report_other(*CONN_SPF, "Spoofed Connections", 0); report_users("VPN Users"); report_summary(*CONN_OUT, "Outbound Connections"); report_summary(*CONN_INS, "Inbound Connections (summary)"); report_summary(*CONN_IGS, "Intra-gateway Connections (summary)"); report_detail(*CONN_INL, "Inbound Connections (detail)"); report_detail(*CONN_IGL, "Intra-gateway Connections (detail)"); report_other(*CONN_DRP, "Dropped Connections", $DROP_MARGIN); open(EMAIL, "| $EMAIL_CMD $EMAIL_ADDRESS"); print EMAIL "Subject: $TITLE\n\n$REPORT"; close(EMAIL); exit 1; } # # read_log_file : Load log data into memory # sub read_log_file { my($file_name, $file_type) = @_; my($line, @arg, $srv); open(LOG, "$ZCAT_CMD $FW1_DIR/$file_name.$TIMESTAMP |"); while($line = ) { chop($line); @arg = split(';', $line); if ($arg[0] eq 'num') { read_header(@arg); next; } $srv = service(@arg); next if (($srv eq 'icmp') && (!$SHOW_ICMP)); if ($line =~ /$SPOOF/) { increment($file_type, *CONN_SPF, $arg[$FW_SRC].';'.$srv.';'.$arg[$FW_DST]); } elsif ($arg[$FW_ACTION] eq 'accept') { if (($arg[$FW_SRC] =~ /^$LOCAL_NET/) && ($arg[$FW_DST] =~ /^$LOCAL_NET/)) { increment($file_type, *CONN_IGL, $srv.';'.$arg[$FW_DST], $arg[$FW_BYTES]); increment($file_type, *CONN_IGS, $srv, $arg[$FW_BYTES]); } elsif ($arg[$FW_DST] =~ /^$LOCAL_NET/) { increment($file_type, *CONN_INL, $srv.';'.$arg[$FW_DST], $arg[$FW_BYTES]); increment($file_type, *CONN_INS, $srv, $arg[$FW_BYTES]); } else { increment($file_type, *CONN_OUT, $srv, $arg[$FW_BYTES]); } } elsif (($arg[$FW_ACTION] eq 'drop') || ($arg[$FW_ACTION] eq 'reject')) { if ($DAYS <= $DROP_DAYS) { increment($file_type, *CONN_DRP, $arg[$FW_SRC].';'.$srv.';'.$arg[$FW_DST]); } } elsif ($arg[$FW_ACTION] eq 'authcrypt') { $USER{$arg[$FW_USER]} .= sprintf("%-20s %-18s %-15s\n", $arg[$FW_USER], "$arg[1] $arg[2]", $arg[$FW_SRC]); } } close(LOG); } # # increment : increment appropriate counter by the of input # sub increment { local($file_type, *vector, $key, $incr) = @_; if ($file_type =~ /log/) { $vector{$key}->{connections}++; } if ($file_type =~ /acc/) { $vector{$key}->{bytes}+=$incr; } } # # report_summary : generate summary report # sub report_summary { local(*vector, $title) = @_; my($key, @arg); $REPORT .= "\n\n$title\n"; $REPORT .= substr($LINE, 1, length($title)); $REPORT .= "\n\nService Connections Gbytes\n"; $REPORT .= "-------------------- -------------- ---------\n"; foreach $key (sort(keys %vector)) { @arg = split(';', $key); $REPORT .= sprintf("%-20s %14d", $arg[0], $vector{$key}->{connections}); if ($vector{$key}->{bytes} > 0) { $REPORT .= sprintf("%10.2f", $vector{$key}->{bytes}/$GBYTE); } $REPORT .= "\n"; } } # # report_detail : generate detailed report # sub report_detail { local(*vector, $title) = @_; my($key, @arg); $REPORT .= "\n\n$title\n"; $REPORT .= substr($LINE, 1, length($title)); $REPORT .= "\n\nService Destination Connections Gbytes\n"; $REPORT .= "-------------------- ---------------------------------- -------------- ---------\n"; foreach $key (sort(keys %vector)) { @arg = split(';', $key); $REPORT .= sprintf("%-20s %-35s %14d", $arg[0], resolv($arg[1]), $vector{$key}->{connections}); if ($vector{$key}->{bytes} > 0) { $REPORT .= sprintf("%10.2f", $vector{$key}->{bytes}/$GBYTE); } $REPORT .= "\n"; } } # # report_other: report dropped connections # sub report_other { local(*vector, $title, $margin) = @_; my($key, @arg); if ($DAYS > $DROP_DAYS) { return; } if (!(keys %vector)) { return; } $REPORT .= "\n\n$title\n"; $REPORT .= substr($LINE, 1, length($title)); $REPORT .= "\n\nSource Destination Service Connections\n"; $REPORT .= "---------------- ---------------- ------------------- ---------------\n"; foreach $key (sort(keys %vector)) { @arg = split(';', $key); if ($vector{$key}->{connections} > $margin) { $REPORT .= sprintf("%-16s %-16s %-20s %14s\n", $arg[0], $arg[2], $arg[1], $vector{$key}->{connections}); } } } # # report_users: report VPN users # sub report_users { local($title) = @_; my($key, @arg); if (!(keys %USER)) { return; } $REPORT .= "\n\n$title\n"; $REPORT .= substr($LINE, 1, length($title)); $REPORT .= "\n\nUser Date Address\n"; $REPORT .= "-------------------- ------------------ ---------------\n"; foreach $key (sort(keys %USER)) { @arg = split(';', $key); $REPORT .= $USER{$key}; } } # # service : normalizes service names - returns named service or port number # and protocol (ftp or 21/tcp) # sub service { my($srv) = $_[$FW_SERVICE]; if (($TR_ROLLUP) && ($srv > 33000) && ($_[$FW_PROTO] eq 'udp')) { $srv = "traceroute"; } elsif ($srv eq '') { $srv = $_[$FW_PROTO]; } elsif ($srv =~ /^\d/) { $srv .= "/$_[$FW_PROTO]"; } return($srv) } # # resolv : returns host name for IP address # sub resolv { my(@nums) = split(/\./, $_[0]); my($addr) = pack('C4', @nums); my($name) = (gethostbyaddr($addr, 2))[0]; if ($name eq '') { return @_; } return substr($name, 0, 34); } # # get_timestamp : Returns a timestamp in the format 21-Dec-1998 # sub get_timestamp { my($now) = time - ($_[0] * 86400); my($monthlist) = "JanFebMarAprMayJunJulAugSepOctNovDec"; my(%days) = (0,Sun,1,Mon,2,Tue,3,Wed,4,Thu,5,Fri,6,Sat); my($sec,$min,$hours,$mday,$mon,$year,$wday,$yday,$dsflag) = localtime($now); my(@yesterday) = ($mday,$mon+1,$year+1900,$days{wday}, substr($monthlist,($mon)*3,3)); sprintf("%02d%s%s", $yesterday[0],$yesterday[4], $yesterday[2]); } # # read_header : Adapt column indicies to match header # sub read_header { my(@args) = @_; my($arg, $i); foreach $arg (@args) { if ($arg eq 'type') { $FW_TYPE = $i; } elsif ($arg eq 'action') { $FW_ACTION = $i; } elsif ($arg eq 'proto') { $FW_PROTO = $i; } elsif ($arg eq 'src') { $FW_SRC = $i; } elsif ($arg eq 'dst') { $FW_DST = $i; } elsif ($arg eq 'service') { $FW_SERVICE = $i; } elsif ($arg eq 'bytes') { $FW_BYTES = $i; } elsif ($arg eq 'user') { $FW_USER = $i; } $i++; } }