]>
git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blob - config/cfgroot/net-traffic-lib.pl
3 # $Id: net-traffic-lib.pl,v 1.4 2005/03/17 11:43:55 dotzball Exp $
5 # Summarize all IP accounting files from start to end time
7 # Copyright (C) 1997 - 2000 Moritz Both
8 # 2001 - 2002 Al Zaharov
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program; if not, write to the Free Software
22 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 # The author can be reached via email: moritz@daneben.de, or by
25 # snail mail: Moritz Both, Im Moore 26, 30167 Hannover,
26 # Germany. Phone: +49-511-1610129
29 # 22 June 2004 By Achim Weber dotzball@users.sourceforge.net
30 # - changed to use it with Net-Traffic Addon
31 # - renamed to avoid issues when calling this file or original ipacsum
32 # - this file is net-traffic-lib.pl for IPCop 1.4.0
39 use POSIX
qw(strftime);
44 $|=1; # line buffering
46 @moff = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 );
48 # =()<$datdelim="@<DATDELIM>@";>()=
49 $datdelim="#-#-#-#-#";
50 # =()<$prefix="@<prefix>@";>()=
52 # =()<$exec_prefix="@<exec_prefix>@";>()=
53 $exec_prefix="${prefix}";
54 # =()<$INSTALLPATH="@<INSTALLPATH>@";>()=
55 $INSTALLPATH="${exec_prefix}/sbin";
56 $datdir="/var/log/ip-acct";
59 $me =~ s
|^.*/([^/]+)$|$1|;
61 $fetchipac="$INSTALLPATH/fetchipac";
62 $rule_regex = ".*"; # match rules with this regex only
64 ## Net-Traffic variables ##
68 my $displayMode = "daily";
69 my ($curMonth, $curYear);
70 ${Traffic
::blue_in
} = 'incoming BLUE';
71 ${Traffic
::green_in
} = 'incoming GREEN';
72 ${Traffic
::orange_in
} = 'incoming ORANGE';
73 ${Traffic
::red_in
} = 'incoming RED';
74 ${Traffic
::blue_out
} = 'outgoing BLUE';
75 ${Traffic
::green_out
} = 'outgoing GREEN';
76 ${Traffic
::orange_out
} = 'outgoing ORANGE';
77 ${Traffic
::red_out
} = 'outgoing RED';
81 $allDaysBytes = shift;
86 $starttime =~ /^(\d\d\d\d)(\d\d)/;
90 # calculate time zone offset in seconds - use difference of output of date
91 # command and time function, round it
92 $tzoffset = time-timegm
(localtime());
93 $machine_name = undef;
95 $starttime = makeunixtime
($starttime);
96 $endtime = makeunixtime
($endtime);
99 # options that we need to pass to fetchipac if we call it.
100 $fetchipac_options = "--directory=$datdir";
102 $endtime = $now if ($endtime > $now);
103 $starttime = 0 if ($starttime < 0);
104 $mystarttime = &makemydailytime
($starttime);
105 $myendtime = &makemydailytime
($endtime);
106 %rule_firstfile = %rule_lastfile = ( );
108 # find out which timestamps we need to read.
109 # remember newest timestamp before starttime so we know when data for
110 # the first file starts
111 # also remember oldest timestamp after end time
112 $newest_timestamp_before_starttime = "";
113 $oldest_timestamp_after_endtime = "";
114 open(DATA
, "$fetchipac $fetchipac_options --timestamps=$starttime,$endtime ".
115 "--machine-output-format|") || die "$me: cant run $fetchipac\n";
116 # the first thing is the timestamp count
123 if (/^(.)\s(\d+)$/) {
126 $newest_timestamp_before_starttime=$ts;
129 $oldest_timestamp_after_endtime=$ts;
132 push(@timestamps, $ts);
135 die "$me: illegal output from $fetchipac: \"$_\"\n";
139 die "$me: illegal output from $fetchipac: \"$_\"\n";
144 push(@timestamps, $oldest_timestamp_after_endtime)
145 if ($oldest_timestamp_after_endtime);
146 unshift(@timestamps, $newest_timestamp_before_starttime)
147 if ($newest_timestamp_before_starttime);
151 # read all data we need and put the data into memory.
154 @days_sorted = sort keys %allDays;
157 ##########################
158 # END OF MAIN PROGRAM
159 ##########################
161 # read all data (@timestmaps contains the timestamps, must be sorted!)
162 # and put the data into our global memory data
163 # structures. special care must be taken with data of the first and
164 # the last timestamps we read, since we only want data which is from our
165 # time frame. Furthermore, data from before and after this time frame
166 # must be preserved in special data structures because we might replace
167 # them (option --replace) and have to write extra data for these times
176 my $curDay = $starttime;
178 # feed the timestamp list to fetchipac on its stdin.
179 socketpair(CHILD
, PARENT
, AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
)
180 or die "socketpair: $!";
182 PARENT
->autoflush(1);
183 my $pid = open(CHILD
, "-|");
184 die "$me: can't fork: $!\n" unless defined $pid;
188 open(FETCHIPAC
, "|$fetchipac $fetchipac_options --record "
189 ."--machine-output-format")
190 or die "$me: cant exec fetchipac\n";
192 #this is much more efficient than the original code (Manfred Weihs)
193 # and it adds more troubles than solves (Al Zakharov)
194 if ($timestamps[0] == $newest_timestamp_before_starttime) {
195 print(FETCHIPAC
$timestamps[1],"-",$timestamps[$count],"\n");
197 print(FETCHIPAC
$timestamps[0],"-",$timestamps[$count-1],"\n");
205 my $laststamp = undef;
206 $laststamp = $newest_timestamp_before_starttime
207 if ($newest_timestamp_before_starttime);
209 $i++ if ($laststamp);
211 # first line of fetchipac output: "ADD"
212 /^ADD\s*$/i or die "$me: bad line from fetchipac: $_\n";
213 # second line of fetchipac output: timestamp no_of_records
214 $_ = <CHILD
> || last;
215 /^(\d+)\s(\d+)$/ or die "$me: bad line from fetchipac: $_\n";
216 my $timestamp = int $1;
217 my $number_of_records = int $2;
220 if ($displayMode =~ /^daily/) {
221 # increment Day aslong current timestamp is not in current Day
222 while ( ($timestamp-$curDay) > 86399) {
228 my @dummy = localtime($timestamp);
229 # increment Month aslong current timestamp is not in current Month
230 while ($curMonth < ($dummy[4]+1) || $curYear<($dummy[5]+1900)) {
232 if ($curMonth > 12) {
236 my $newMonth = $curYear;
237 $newMonth .= $curMonth < 10 ?
"0".$curMonth."01" : $curMonth."01";
239 $curDay = &makeunixtime
($newMonth);
243 if ($timestamp < $starttime) {
244 # this record is too old, we dont need the data.
245 # However, the timestamp gives us a clue on the
246 # time period the next item covers.
252 my $data = &read_data_record
(CHILD
, $number_of_records);
254 if ($do_collect && $in_time == 0) {
255 # the data is from after starttime. if it is the
256 # first one, split the data (if we know for how
257 # long this data is valid, and if $laststamp is not
258 # equal to $starttime in which case the split is
259 # redundant). If we don't have a clue about the
260 # last file time before our first file was created,
261 # we do not know how much of the file data is in our
262 # time frame. we assume everything belongs to us.
264 # if ($laststamp && $laststamp != $starttime) {
265 if ($laststamp && $laststamp != $newest_timestamp_before_starttime) {
266 my $newdata = &split_data
($data,
267 $laststamp, $timestamp, $starttime);
268 $glb_data_before = $data;
270 $laststamp = $starttime;
274 if ($timestamp > $endtime) {
275 # this data is too new, but the data in it may have
276 # begun within our time frame. (if endtime eq laststamp
277 # we do a redundant split here, too - it works for now
278 # and --replace relies on it, but it is ugly.)
279 if ($after_time == 0) {
283 &split_data
($data,$laststamp,$timestamp,$endtime);
288 $do_collect = 0; # just too new.
293 &collect_data
($data, $i, $curDay);
295 $laststamp = $timestamp;
302 # split the data in $1 (format as from read_data) into a pair of two
303 # such data sets. The set referenced to as $1 will afterwards contain
304 # the first part of the data, another set which is returned contains
305 # the second part of the data.
306 # interpret the data as having start time=$2 and end time=$3 and split
314 # calculate factors for multiplications
316 my $uperiod = $mend - $ust;
317 my $usplit = $msplit - $ust;
320 # hmmm? die Daten sind rueckwaerts???
321 $uperiod = -$uperiod;
327 elsif ($usplit > $uperiod) {
331 $fac1 = $usplit / $uperiod;
334 # $fac1 now says us how much weight the first result has.
335 # initialize the set we will return.
338 foreach $set (@
$data) {
339 my ($rule, $bytes, $pkts) = @
$set;
340 $$set[1] = int($bytes * $fac1 + 0.5);
341 $$set[2] = int($pkts * $fac1 + 0.5);
342 push(@ret, [ $rule, $bytes - $$set[1], $pkts - $$set[2] ]);
347 # put data from one file into global data structures
348 # must be called in correct sorted file name order to set rules_lastfile
349 # and rules_firstfile (which are currently useless)
351 # $1=index number of file; $2 = reference to array with data from file
353 my($filedata, $ifile, $i, $day);
359 # if day first appeared in this file, initialize its
361 if (!defined($allDays{$day})) {
362 return if (&init_filter_id
($day));
363 $allDays{$day} = $rulenumber++;
366 for ($i=0; $i<=$#$filedata; $i++) {
367 my $set = $$filedata[$i];
369 my $bytes = $$set[1];
377 $allDaysBytes->{$day}{$rule} += $bytes;
381 # initialize data variables for a new rule - if it is new
385 if (!defined $allDaysBytes->{$s}) {
386 if ($displayMode =~ /^daily/) {
387 my $newDay = &makemydailytime
($s);
388 $newDay =~ /^\d\d\d\d-(\d\d)-\d\d$/;
390 return 1 if ($1 > $curMonth && $displayMode ne "daily_multi");
392 $allDaysBytes->{$s}{'Day'} = $newDay;
395 $allDaysBytes->{$s}{'Day'} = &makemymonthlytime
($s);
397 $allDaysBytes->{$s}{${Traffic
::blue_in
}} = int(0);
398 $allDaysBytes->{$s}{${Traffic
::green_in
}} = int(0);
399 $allDaysBytes->{$s}{${Traffic
::orange_in
}} = int(0);
400 $allDaysBytes->{$s}{${Traffic
::red_in
}} = int(0);
401 $allDaysBytes->{$s}{${Traffic
::blue_out
}} = int(0);
402 $allDaysBytes->{$s}{${Traffic
::green_out
}} = int(0);
403 $allDaysBytes->{$s}{${Traffic
::orange_out
}} = int(0);
404 $allDaysBytes->{$s}{${Traffic
::red_out
}} = int(0);
409 # read data record from filehandle $1
410 # number of records is $2
411 # Return value: reference to array a of length n;
412 # n is the number of rules
413 # each field in a is an array aa with 3 fields
414 # the fields in arrays aa are: [0]=name of rule; [1]=byte count;
416 # function does not use global variables
417 sub read_data_record
{
418 my($file, $number_of_records, $beforedata, $indata, $i, $irec);
419 my($pkts, $bytes, $rule);
423 $number_of_records = shift;
427 for($irec = 0; $irec < $number_of_records; $irec++) {
430 /^\(\s*(.*)$/ or die "$me: bad line from fetchipac (expecting machine name): $_\n";
431 $machine_name = $1; # remember final machine name
433 last if (/^\)$/); # terminating line ')'
434 /^(\d+)\s(\d+)\s\|(.*)\|$/
435 or die "$me: bad line from fetchipac (expecting rule item): $_\n";
439 if ($rule =~ /$rule_regex/) {
440 push(@result, [ $rule, $bytes, $pkts]);
444 # read another emtpy line (data format consistency)
446 die "$me: bad data from fetchipac (expected emtpy line): $_\n"
451 # given a string in format YYYYMMDD[hh[mm[ss]]], make unix time
452 # use time zone offset $tzoffset (input=wall clock time, output=UTC)
454 my($y, $m, $d, $h, $i, $e);
458 if ($s =~ /^(\d\d\d\d)(\d\d)(\d\d)/) {
459 ($y, $m, $d) = ($1, $2, $3);
460 if ($s =~ /^\d\d\d\d\d\d\d\d-?(\d\d)/) {
462 if ($s =~ /^\d\d\d\d\d\d\d\d-?\d\d(\d\d)/) {
464 if ($s =~ /^\d\d\d\d\d\d\d\d-?\d\d\d\d(\d\d)/) {
475 $s = (($y)*365) + int(($y+2)/4) + $moff[$m-1] + $d-1;
476 $s-- if (($y+2)%4 == 0 && $m < 3);
477 $s*86400 + $h*3600 + $i*60 + $e + $tzoffset;
480 # return the given unix time in localtime in "mydaily" time format
481 sub makemydailytime
{
484 my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
486 return sprintf("%04d-%02d-%02d", 1900+$year, $mon+1, $mday);
489 # return the given unix time in localtime in "mymonthly" time format
490 sub makemymonthlytime
{
493 my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
495 return sprintf("%04d-%02d", 1900+$year, $mon+1);