http://www.perlmonks.org?node_id=51111
Category: Networking Code
Author/Contact Info Bjorn <bjornn@ihug.co.nz>
Description: This parses a log file of ipchains IP accounting data and spits it out as a csv. I use this at work for statistical and billing purposes. It is still a work in progress (but it does work) so I'm interested in any comments. The log file is created from a shell script run from a cron, which you will find at the end of the code.
#!/usr/bin/perl

use strict;
use warnings;

my $logfile = $ARGV[0];

my $start_date;
my $end_date;
my %log_totals;


open (FILE< $logfile) or die "Could not open $logfile: $!\n";

for (<FILE>) {
        if (/^\d+/) {
                ($start_date) = m/^(\d+)/o unless ($start_date);
                ($end_date) = m/^(\d+)/o;
        } elsif (/^\s+\d+/o) {
                my ($bytes,$source,$destination,$ports) =
                m/
                        ^\s+\d+\s+(\d+)                 # pkts & bytes
                        .+                              # bla bla bla
                        \s+(\S+)                        # source
                        \s+(\S+)                        # destination
                        \s+(n\/a|\w+\s+->\s+\w+)$       # ports
                /ox;

                my ($src_port,$dest_port) = ('any','any');

                ($src_port,$dest_port) = $ports =~ m/(\w+)\s+->\s+(\w+
+)/o
                        unless ($ports eq 'n/a');
                &data2hash($bytes,$source,$src_port,$destination,$dest
+_port);
                # print "$bytes,$source,$src_port,$destination,$dest_p
+ort\n";
        }
}

my $start = localtime($start_date);
my $end = localtime($end_date);

print "From: $start\n";
print "To: $end\n";
print "Source Address,Source Port,Destination Address,Destination Port
+,Bytes\n";
for my $key (keys %log_totals) {
        print "$key,$log_totals{$key}\n";
}

sub data2hash {
        my ($bytes,$src_addr,$src_port,$dest_addr,$dest_port) = @_;

        if ($src_addr ne 'anywhere') { # we have a src address match
                if ($src_port ne 'any') { # we have a src port match
                        $log_totals{"$src_addr,$src_port,,"} += $bytes
+;
                } elsif ($dest_port ne 'any') { # we have a dest port 
+match
                        $log_totals{"$src_addr,,,$dest_port"} += $byte
+s;
                } else { # only src address match
                        $log_totals{"$src_addr,,,"} += $bytes;
                }
        } elsif ($dest_addr ne 'anywhere') { # we have a dest address 
+match
                if ($src_port ne 'any') { # we have a src port match
                        $log_totals{",$src_port,$dest_addr,"} += $byte
+s;
                } elsif ($dest_port ne 'any') { # we have a dest port 
+match
                        $log_totals{",,$dest_addr,$dest_port"} += $byt
+es;
                } else { # only dest address match
                        $log_totals{",,$dest_addr,"} += $bytes;
                }
        } else { # shouldn't get here
                print STDERR "eh?\n";
        }
}

close FILE;


#!/bin/bash
/bin/date +%s >>/var/log/ip-acc/ip-acc.log
/sbin/ipchains -L ip-acc -v -x >>/var/log/ip-acc/ip-acc.log
/sbin/ipchains -Z ip-acc
Replies are listed 'Best First'.
Re: IP Accounting parser
by turnstep (Parson) on Jan 12, 2001 at 02:52 UTC

    Erk! This code:

    open FILE, $logfile || die $!;
    does not act the way you want it to. Because the || has a higher precedence than the comma, what actually happens is this:
    open FILE, ($logfile || die $!);
    see perlop for the complete precedence list.

    If you really want to use the || then put parenthesis around things like so:

    open (FILE, $logfile) || die "Could not open $logfile: $!\n";

    Better yet, use the super-low-priority or, which allows you to acheive your original effect:

    open FILE, $logfile or die "Could not open $logfile: $!\n";

    Best of all, be correct and non-ambigous and use both:

    open (FILE< $logfile) or die "Could not open $logfile: $!\n";

      I always get || and or mixed up, I will attempt to burn the precedence list into my brain.

        It may help to think of or and and as 'lowercase', and thus 'lower precedence' as opposed to || and && which require the use of the shift key, and therefore are 'uppercase' and 'upper precedence'

        You could also just remember that nothing is lower in precedence than and and or