Handle hostnames with upper-case letters
[webmin.git] / bandwidth / bandwidth-lib.pl
1 # Functions for parsing the system log for iptables entries
2 # XXX ipf support
3 #       XXX ipf missing some packets? large FTP transfer?
4 # XXX option to specify ports considered 'server' (by ranges)
5 #       XXX option to make ports without names not server ports
6 #       XXX use is_server_port function
7
8 BEGIN { push(@INC, ".."); };
9 use WebminCore;
10 &init_config();
11 if (&foreign_installed("syslog-ng")) {
12         &foreign_require("syslog-ng", "syslog-ng-lib.pl");
13         $syslog_module = "syslog-ng";
14         }
15 elsif (&foreign_installed("syslog")) {
16         &foreign_require("syslog", "syslog-lib.pl");
17         $syslog_module = "syslog";
18         }
19 else {
20         $syslog_module = undef;
21         }
22 &foreign_require("cron", "cron-lib.pl");
23 &foreign_require("net", "net-lib.pl");
24
25 %access = &get_module_acl();
26
27 $bandwidth_log = $config{'bandwidth_log'} || "/var/log/bandwidth";
28 $hours_dir = $config{'bandwidth_dir'} || "$module_config_directory/hours";
29 $cron_cmd = "$module_config_directory/rotate.pl";
30 $pid_file = "$module_config_directory/rotate.pid";
31
32 # list_hours()
33 # Returns a list of all hours for which traffic is available
34 sub list_hours
35 {
36 opendir(DIR, $hours_dir);
37 local @rv = grep { $_ =~ /^\d+$/ } readdir(DIR);
38 closedir(DIR);
39 return @rv;
40 }
41
42 # get_hour(num)
43 sub get_hour
44 {
45 if (!defined($hour_cache{$_[0]})) {
46         local $file = "$hours_dir/$_[0]";
47         local %hour;
48         if (&read_file($file, \%hour)) {
49                 $hour_cache{$_[0]} = \%hour;
50                 }
51         else {
52                 $hour_cache{$_[0]} = { 'hour' => $_[0] };
53                 }
54         }
55 return $hour_cache{$_[0]};
56 }
57
58 # save_hour(hour)
59 sub save_hour
60 {
61 mkdir($hours_dir, 0755);
62 local $file = "$hours_dir/$_[0]->{'hour'}";
63 &write_file($file, $_[0]);
64 }
65
66 # find_rule(&table, chain, interface, dir)
67 sub find_rule
68 {
69 local ($table, $chain, $iface, $dir) = @_;
70 local $r;
71 local $dd = $dir eq "i" ? "IN" : "OUT";
72 foreach $r (@{$table->{'rules'}}) {
73         next if ($r->{'chain'} ne $chain);
74         local $da = $r->{$dir};
75         if ($iface) {
76                 next if ($da->[1] ne $iface);
77                 }
78         else {
79                 next if (!$da);
80                 }
81         next if ($r->{'j'}->[1] ne 'LOG');
82         next if ($r->{'args'} !~ /\-\-log\-prefix\s+BANDWIDTH_\Q$dd\E:/ &&
83                  $r->{'args'} !~ /\-\-log\-prefix\s+"BANDWIDTH_\Q$dd\E:"/ &&
84                  $r->{'logprefix'}->[1] ne "BANDWIDTH_$dd");
85         return $r;
86         }
87 return undef;
88 }
89
90 # find_sysconf(&conf)
91 # Returns the syslog entry for kernel debug messages to the log file
92 sub find_sysconf
93 {
94 local ($conf) = @_;
95 local $c;
96 local @ll = &get_loglevel();
97 foreach $c (@$conf) {
98         next if (!$c->{'active'});
99         next if ($c->{'file'} ne $bandwidth_log);
100         next if ($c->{'sel'}->[0] ne $ll[0]);
101         return $c;
102         }
103 return undef;
104 }
105
106 # find_sysconf_ng(&conf)
107 # Returns the destination, filter and log objects for kernel debug messages
108 sub find_sysconf_ng
109 {
110 local ($conf) = @_;
111 local @dests = &syslog_ng::find("destination", $conf);
112 local ($dest) = grep { $_->{'value'} eq "d_bandwidth" } @dests;
113 local @filters = &syslog_ng::find("filter", $conf);
114 local ($filter) = grep { $_->{'value'} eq "f_bandwidth" } @filters;
115 local @logs = &syslog_ng::find("log", $conf);
116 local $log;
117 if ($dest && $filter) {
118         foreach my $l (@logs) {
119                 local ($ldest) = &syslog_ng::find("destination",
120                                                   $l->{'members'});
121                 local ($lfilter) = &syslog_ng::find("filter",
122                                                     $l->{'members'});
123                 if ($ldest->{'value'} eq $dest->{'value'} &&
124                     $lfilter->{'value'} eq $filter->{'value'}) {
125                         $log = $l;
126                         last;
127                         }
128                 }
129         }
130 return ($dest, $filter, $log);
131 }
132
133 # find_cron_job()
134 # Returns the cron job used for bandwidth counting, or undef
135 sub find_cron_job
136 {
137 local @jobs = &cron::list_cron_jobs();
138 local ($job) = grep { $_->{'user'} eq 'root' &&
139                       $_->{'command'} eq $cron_cmd &&
140                       $_->{'active'} } @jobs;
141 return $job;
142 }
143
144 # date_input(day, month, year, prefix)
145 sub date_input
146 {
147 return &ui_textbox("$_[3]_day", $_[0], 2)."/".
148        &ui_select("$_[3]_month", $_[1],
149                   [ map { [ $_, $text{"smonth_".$_} ] } (1 .. 12) ])."/". 
150        &ui_textbox("$_[3]_year", $_[2], 4).
151        &date_chooser_button("$_[3]_day", "$_[3]_month", "$_[3]_year");
152 }
153
154 # hourmin_input(hour, min, prefix)
155 sub hourmin_input
156 {
157 return &ui_textbox("$_[2]_hour", $_[0], 2).":".
158        &ui_textbox("$_[2]_min", $_[1], 2);
159 }
160
161 # detect_firewall_system()
162 # Guesses which firewall is installed
163 sub detect_firewall_system
164 {
165 local $m;
166 foreach $m ("shorewall", "firewall", "ipfw", "ipfilter") {
167         return $m if (&check_firewall_system($m));
168         }
169 return undef;
170 }
171
172 # check_firewall_system(type)
173 # Returns 1 if some firewall is installed
174 sub check_firewall_system
175 {
176 # XXX shorewall check should look for actual rules?
177 return &foreign_installed($_[0], 1);
178 }
179
180 sub check_rules
181 {
182 local $cfunc = "check_".$config{'firewall_system'}."_rules";
183 return &$cfunc(@_);
184 }
185
186 sub setup_rules
187 {
188 local $sfunc = "setup_".$config{'firewall_system'}."_rules";
189 return &$sfunc(@_);
190 }
191
192 sub delete_rules
193 {
194 local $dfunc = "delete_".$config{'firewall_system'}."_rules";
195 return &$dfunc(@_);
196 }
197
198 sub process_line
199 {
200 local $pfunc = "process_".$config{'firewall_system'}."_line";
201 return &$pfunc(@_);
202 }
203
204 sub pre_process
205 {
206 local $pfunc = "pre_".$config{'firewall_system'}."_process";
207 if (defined(&$pfunc)) {
208         &$pfunc(@_);
209         }
210 }
211
212 sub get_loglevel
213 {
214 local $lfunc = "get_".$config{'firewall_system'}."_loglevel";
215 return &$lfunc(@_);
216 }
217
218 # is_server_port(num)
219 sub is_server_port
220 {
221 }
222
223 ############### functions for IPtables #################
224
225 # check_firewall_rules()
226 # Returns 1 if the IPtables rules needed are setup, 0 if not
227 sub check_firewall_rules
228 {
229 &foreign_require("firewall", "firewall-lib.pl");
230 local @tables = &firewall::get_iptables_save();
231 local ($filter) = grep { $_->{'name'} eq 'filter' } @tables;
232
233 local $inrule = &find_rule($filter, "INPUT", $config{'iface'}, "i");
234 local $outrule = &find_rule($filter, "OUTPUT", $config{'iface'}, "o");
235 local $fwdinrule = &find_rule($filter, "FORWARD", $config{'iface'}, "i");
236 local $fwdoutrule = &find_rule($filter, "FORWARD", $config{'iface'}, "o");
237 return $inrule && $outrule && $fwdinrule && $fwdoutrule;
238 }
239
240 # setup_firewall_rules(iface)
241 # If any IPtables rules are missing, add them
242 sub setup_firewall_rules
243 {
244 &foreign_require("firewall", "firewall-lib.pl");
245 local @tables = &firewall::get_iptables_save();
246 local ($filter) = grep { $_->{'name'} eq 'filter' } @tables;
247 $filter ||= { 'name' => 'filter',
248               'defaults' => { 'INPUT' => 'ACCEPT',
249                               'OUTPUT' => 'ACCEPT',
250                               'FORWARD' => 'ACCEPT' },
251               'rules' => [ ],
252             };
253 local $inrule = &find_rule($filter, "INPUT", $config{'iface'}, "i");
254 local $outrule = &find_rule($filter, "OUTPUT", $config{'iface'}, "o");
255 local $fwdinrule = &find_rule($filter, "FORWARD", $config{'iface'}, "i");
256 local $fwdoutrule = &find_rule($filter, "FORWARD", $config{'iface'}, "o");
257 local $missingrule = !$inrule || !$outrule || !$fwdinrule || !$fwdoutrule;
258 if (!$inrule) {
259         splice(@{$filter->{'rules'}}, 0, 0,
260                { 'chain' => 'INPUT',
261                  'j' => [ undef, 'LOG' ],
262                  'i' => [ undef, $_[0] ],
263                  'args' => "--log-level 7 --log-prefix BANDWIDTH_IN:" });
264         }
265 if (!$outrule) {
266         splice(@{$filter->{'rules'}}, 0, 0,
267                { 'chain' => 'OUTPUT',
268                  'j' => [ undef, 'LOG' ],
269                  'o' => [ undef, $_[0] ],
270                  'args' => "--log-level 7 --log-prefix BANDWIDTH_OUT:" });
271         }
272 if (!$fwdinrule) {
273         splice(@{$filter->{'rules'}}, 0, 0,
274                { 'chain' => 'FORWARD',
275                  'j' => [ undef, 'LOG' ],
276                  'i' => [ undef, $_[0] ],
277                  'args' => "--log-level 7 --log-prefix BANDWIDTH_IN:" });
278         }
279 if (!$fwdoutrule) {
280         splice(@{$filter->{'rules'}}, 0, 0,
281                { 'chain' => 'FORWARD',
282                  'j' => [ undef, 'LOG' ],
283                  'o' => [ undef, $_[0] ],
284                  'args' => "--log-level 7 --log-prefix BANDWIDTH_OUT:" });
285         }
286
287 if ($missingrule) {
288         # Save and apply
289         &lock_file($firewall::iptables_save_file);
290         &firewall::run_before_command();
291         &firewall::save_table($filter);
292         &firewall::run_after_command();
293         &unlock_file($firewall::iptables_save_file);
294         return &firewall::apply_configuration();
295         }
296 return undef;
297 }
298
299 # delete_firewall_rules()
300 # Delete firewall rules for bandwidth logging
301 sub delete_firewall_rules
302 {
303 &foreign_require("firewall", "firewall-lib.pl");
304 local @tables = &firewall::get_iptables_save();
305 local ($filter) = grep { $_->{'name'} eq 'filter' } @tables;
306 local $inrule = &find_rule($filter, "INPUT", $config{'iface'}, "i");
307 local $outrule = &find_rule($filter, "OUTPUT", $config{'iface'}, "o");
308 local $fwdinrule = &find_rule($filter, "FORWARD", $config{'iface'}, "i");
309 local $fwdoutrule = &find_rule($filter, "FORWARD", $config{'iface'}, "o");
310 local $anyrule = $inrule || $outrule || $fwdinrule || $fwdoutrule;
311 if ($anyrule) {
312         @{$filter->{'rules'}} = grep { $_ ne $inrule &&
313                                        $_ ne $outrule &&
314                                        $_ ne $fwdinrule &&
315                                        $_ ne $fwdoutrule }@{$filter->{'rules'}};
316
317         # Save and apply firewall
318         &lock_file($firewall::iptables_save_file);
319         &firewall::run_before_command();
320         &firewall::save_table($filter);
321         &firewall::run_after_command();
322         &unlock_file($firewall::iptables_save_file);
323         return &firewall::apply_configuration();
324         }
325 return undef;
326 }
327
328 # process_firewall_line(line, &hours, time-now)
329 # Process an IPtables firewall line, and returns 1 if successful
330 sub process_firewall_line
331 {
332 local ($line, $hours, $time_now) = @_;
333 if ($line =~ /^(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+).*BANDWIDTH_(IN|OUT):(IN=.*)/) {
334         # Found a valid line
335         local ($mon, $day, $hr, $min, $sec) = ($1, $2, $3, $4, $5);
336         local $dir = lc($6);
337         local %line;
338         local $w;
339         foreach $w (split(/\s+/, $7)) {
340                 ($n, $v) = split(/=/, $w);
341                 if ($n) {
342                         $line{lc($n)} = $v;
343                         }
344                 }
345
346         # Work out the real time
347         local $tm = timelocal($sec, $min, $hr, $day,
348                               &month_to_number($mon), $time_now[5]);
349         if ($tm > $time_now + 24*60*60) {
350                 # Was really last year
351                 $tm = timelocal($sec, $min, $hr, $day,
352                                 &month_to_number($mon), $time_now[5]-1);
353                 }
354         local $htm = int($tm/(60*60));
355
356         # Update the appropriate counters
357         local $hour = &get_hour($htm);
358         if (&indexof($hour, @$hours) < 0) {
359                 push(@$hours, $hour);
360                 }
361         local $port;
362         if ($line{'proto'} eq 'TCP' || $line{'proto'} eq 'UDP') {
363                 if ($dir eq "in") {
364                         $port = '_'.$line{'dpt'}.'_'.$line{'spt'};
365                         }
366                 else {
367                         $port = '_'.$line{'spt'}.'_'.$line{'dpt'};
368                         }
369                 }
370         local $host = $dir eq "in" ? $line{'dst'} : $line{'src'};
371         local $key = $host.'_'.$line{'proto'}.$port;
372         local ($in, $out) = split(/ /, $hour->{$key});
373         if ($dir eq "in") {
374                 $in += $line{'len'};
375                 }
376         else {
377                 $out += $line{'len'};
378                 }
379         $hour->{$key} = int($in)." ".int($out);
380         return 1;
381         }
382 else {
383         return 0;
384         }
385 }
386
387 # get_firewall_loglevel()
388 sub get_firewall_loglevel
389 {
390 return ( "kern.=debug" );
391 }
392
393 ############### functions for ipfw #################
394
395 sub check_ipfw_rules
396 {
397 &foreign_require("ipfw", "ipfw-lib.pl");
398 local $rules = &ipfw::get_config();
399 local $rule = &find_ipfw_rule($rules, $config{'iface'});
400 return $rule ? 1 : 0;
401 }
402
403 # setup_ipfw_rules(iface)
404 # Add the logging rule used for ipfw
405 sub setup_ipfw_rules
406 {
407 &foreign_require("ipfw", "ipfw-lib.pl");
408 local $rules = &ipfw::get_config();
409 local $rule = &find_ipfw_rule($rules, $config{'iface'});
410 if (!$rule) {
411         # Add the rule
412         local $num = 100;
413         if (@$rules && $rules->[0]->{'num'} < 100) {
414                 $num = int($rules->[0]->{'num'} / 2);
415                 }
416         $rule = { 'action' => 'count',
417                   'log' => 1,
418                   'proto' => 'all',
419                   'from' => 'any',
420                   'to' => 'any',
421                   'num' => $num,
422                   'via' => $_[0] };
423         splice(@$rules, 0, 0, $rule);
424         &ipfw::save_config($rules);
425
426         # Apply config
427         return &ipfw::apply_rules($rules);
428         }
429 return undef;
430 }
431
432 # delete_ipfw_rules()
433 # Delete the logging rule used for ipfw
434 sub delete_ipfw_rules
435 {
436 &foreign_require("ipfw", "ipfw-lib.pl");
437 local $rules = &ipfw::get_config();
438 local $rule = &find_ipfw_rule($rules, $config{'iface'});
439 if ($rule) {
440         @$rules = grep { $_ ne $rule } @$rules;
441         &ipfw::save_config($rules);
442         return &ipfw::apply_rules();
443         }
444 return undef;
445 }
446
447 # find_ipfw_rule(&rules, iface)
448 sub find_ipfw_rule
449 {
450 local ($rule) = grep { ($_->{'action'} eq 'allow' ||
451                         $_->{'action'} eq 'count') &&
452                        $_->{'log'} &&
453                        ($_->{'proto'} eq 'all' || $_->{'proto'} eq 'ip') &&
454                        $_->{'from'} eq 'any' &&
455                        $_->{'to'} eq 'any' &&
456                        $_->{'via'} eq $_[1] } @{$_[0]};
457 return $rule;
458 }
459
460 # pre_ipfw_process()
461 # Called before processing of the logs, to get the average packet size
462 sub pre_ipfw_process
463 {
464 &foreign_require("ipfw", "ipfw-lib.pl");
465 local $active = &ipfw::get_config("$ipfw::config{'ipfw'} show |", \$out);
466 local $rule = &find_ipfw_rule($active, $config{'iface'});
467 if ($rule && $rule->{'count1'}) {
468         $average_packet_size = $rule->{'count2'} / $rule->{'count1'};
469         system("$ipfw::config{'ipfw'} zero $rule->{'num'} >/dev/null 2>&1");
470         }
471 else {
472         $average_packet_size = 1;
473         }
474 }
475
476 # process_ipfw_line(line, &hours, time-now)
477 # Process a BSD IPFW firewall line, and returns 1 if successful
478 sub process_ipfw_line
479 {
480 local ($line, $hours, $time_now) = @_;
481 if ($line =~ /^(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+).*ipfw:\s+\S+\s+(Accept|Count)\s+(\S+)\s+(\S+)\s+(\S+)\s+(in|out)\s+via\s+(\S+)/) {
482         # Found a valid line
483         local ($mon, $day, $hr, $min, $sec) = ($1, $2, $3, $4, $5);
484         local ($proto, $src, $dest, $dir, $iface) = ($7, $8, $9, $10, $11);
485         local ($srchost, $srcport) = split(/:/, $src);
486         local ($desthost, $destport) = split(/:/, $dest);
487         $proto =~ s/:.*//;
488         return undef if ($iface ne $config{'iface'});
489
490         # Work out the real time
491         local $tm = timelocal($sec, $min, $hr, $day,
492                               &month_to_number($mon), $time_now[5]);
493         if ($tm > $time_now + 24*60*60) {
494                 # Was really last year
495                 $tm = timelocal($sec, $min, $hr, $day,
496                                 &month_to_number($mon), $time_now[5]-1);
497                 }
498         local $htm = int($tm/(60*60));
499
500         # Update the appropriate counters
501         local $hour = &get_hour($htm);
502         if (&indexof($hour, @$hours) < 0) {
503                 push(@$hours, $hour);
504                 }
505         local $port;
506         if ($proto eq 'TCP' || $proto eq 'UDP') {
507                 if ($dir eq "in") {
508                         $port = '_'.$destport.'_'.$srcport;
509                         }
510                 else {
511                         $port = '_'.$srcport.'_'.$destport;
512                         }
513                 }
514         local $host = $dir eq "in" ? $desthost : $srchost;
515         local $key = $host.'_'.$proto.$port;
516         local ($in, $out) = split(/ /, $hour->{$key});
517         if ($dir eq "in") {
518                 $in += $average_packet_size;
519                 }
520         else {
521                 $out += $average_packet_size;
522                 }
523         $hour->{$key} = int($in)." ".int($out);
524         return 1;
525         }
526 else {
527         return 0;
528         }
529 }
530
531
532
533 # get_ipfw_loglevel()
534 sub get_ipfw_loglevel
535 {
536 return ( "security.*", "kern.debug" );
537 }
538
539 ############### functions for Shorewall #################
540
541 sub check_shorewall_rules
542 {
543 &foreign_require("shorewall", "shorewall-lib.pl");
544 local $lref = &read_file_lines("$shorewall::config{'config_dir'}/start");
545 local $rule1 = &find_shorewall_rule($lref, "INPUT", "-i", $config{'iface'});
546 local $rule2 = &find_shorewall_rule($lref, "FORWARD", "-i", $config{'iface'});
547 local $rule3 = &find_shorewall_rule($lref, "FORWARD", "-o", $config{'iface'});
548 local $rule4 = &find_shorewall_rule($lref, "OUTPUT", "-o", $config{'iface'});
549 return defined($rule1) && defined($rule2) && defined($rule3) && defined($rule4);
550 }
551
552 # setup_shorewall_rules(iface)
553 # Add lines to the Shorewall start script to add logging IPtables rules
554 sub setup_shorewall_rules
555 {
556 &foreign_require("shorewall", "shorewall-lib.pl");
557 local $lref = &read_file_lines("$shorewall::config{'config_dir'}/start");
558 local $rule1 = &find_shorewall_rule($lref, "INPUT", "-i", $_[0]);
559 local $rule2 = &find_shorewall_rule($lref, "FORWARD", "-i", $_[0]);
560 local $rule3 = &find_shorewall_rule($lref, "FORWARD", "-o", $_[0]);
561 local $rule4 = &find_shorewall_rule($lref, "OUTPUT", "-o", $_[0]);
562 local $gotall = defined($rule1) && defined($rule2) &&
563                 defined($rule3) && defined($rule4);
564 if (!defined($rule1)) {
565         push(@$lref, "run_iptables -I INPUT -i $_[0] -j LOG --log-prefix BANDWIDTH_IN: --log-level debug");
566         }
567 if (!defined($rule2)) {
568         push(@$lref, "run_iptables -I FORWARD -i $_[0] -j LOG --log-prefix BANDWIDTH_IN: --log-level debug");
569         }
570 if (!defined($rule3)) {
571         push(@$lref, "run_iptables -I FORWARD -o $_[0] -j LOG --log-prefix BANDWIDTH_OUT: --log-level debug");
572         }
573 if (!defined($rule4)) {
574         push(@$lref, "run_iptables -I OUTPUT -o $_[0] -j LOG --log-prefix BANDWIDTH_OUT: --log-level debug");
575         }
576 &flush_file_lines();
577
578 if (!$gotall) {
579         # Apply config with a shorewall restart
580         &shorewall::run_before_apply_command();
581         local $out = &backquote_logged("$shorewall::config{'shorewall'} restart 2>&1");
582         return "<pre>$out</pre>" if ($?);
583         &shorewall::run_after_apply_command();
584         }
585 return undef;
586 }
587
588 sub delete_shorewall_rules
589 {
590 &foreign_require("shorewall", "shorewall-lib.pl");
591 local $lref = &read_file_lines("$shorewall::config{'config_dir'}/start");
592 local $rule1 = &find_shorewall_rule($lref, "INPUT", "-i", $config{'iface'});
593 splice(@$lref, $rule1, 1) if (defined($rule1));
594 local $rule2 = &find_shorewall_rule($lref, "FORWARD", "-i", $config{'iface'});
595 splice(@$lref, $rule2, 1) if (defined($rule2));
596 local $rule3 = &find_shorewall_rule($lref, "FORWARD", "-o", $config{'iface'});
597 splice(@$lref, $rule3, 1) if (defined($rule3));
598 local $rule4 = &find_shorewall_rule($lref, "OUTPUT", "-o", $config{'iface'});
599 splice(@$lref, $rule4, 1) if (defined($rule4));
600 &flush_file_lines();
601
602 if (defined($rule1) || defined($rule2) || defined($rule3) || defined($rule4)) {
603         # Apply config with a shorewall restart
604         &shorewall::run_before_apply_command();
605         local $out = &backquote_logged("$shorewall::config{'shorewall'} restart 2>&1");
606         return "<pre>$out</pre>" if ($?);
607         &shorewall::run_after_apply_command();
608         }
609 return undef;
610 }
611
612 # find_shorewall_rule(&lref, chain, dir, iface)
613 # Returns the line indexes of the shorewall start script entries that add
614 # the extra logging IPtables rules
615 sub find_shorewall_rule
616 {
617 local ($lref, $chain, $dir, $iface) = @_;
618 local $io = $dir eq "-i" ? "BANDWIDTH_IN:" : "BANDWIDTH_OUT:";
619 local (@rv, $i);
620 for($i=0; $i<@$lref; $i++) {
621         if ($lref->[$i] =~ /^run_iptables\s+-I\s+$chain\s+$dir\s+$iface\s+-j\s+LOG\s+--log-prefix\s+$io\s+--log-level\s+debug/) {
622                 return $i;
623                 }
624         }
625 return undef;
626 }
627
628 # get_shorewall_loglevel()
629 sub get_shorewall_loglevel
630 {
631 return ( "kern.=debug" );
632 }
633
634 sub process_shorewall_line
635 {
636 return &process_firewall_line(@_);
637 }
638
639 ############### functions for ipfilter #################
640
641 sub check_ipfilter_rules
642 {
643 &foreign_require("ipfilter", "ipfilter-lib.pl");
644 local $rules = &ipfilter::get_config();
645 local $rule1 = &find_ipfilter_rule($rules, $config{'iface'}, "in");
646 local $rule2 = &find_ipfilter_rule($rules, $config{'iface'}, "out");
647 return $rule1 && $rule2;
648 }
649
650 # setup_ipfilter_rules(iface)
651 # Add the logging rule used for ipfilter
652 sub setup_ipfilter_rules
653 {
654 &foreign_require("ipfilter", "ipfilter-lib.pl");
655 local $rules = &ipfilter::get_config();
656 local $rule1 = &find_ipfilter_rule($rules, $_[0], "in");
657 local $rule2 = &find_ipfilter_rule($rules, $_[0], "out");
658 local $gotall = $rule1 && $rule2;
659 if (!$rule1) {
660         $rule1 = { 'action' => 'log',
661                    'on' => $_[0],
662                    'log-level' => 'local7.debug',
663                    'dir' => 'in',
664                    'all' => 1,
665                    'active' => 1 };
666         if (@$rules) {
667                 &ipfilter::insert_rule($rule1, $rules->[0]);
668                 }
669         else {
670                 &ipfilter::create_rule($rule1);
671                 }
672         }
673 if (!$rule2) {
674         $rule2 = { 'action' => 'log',
675                    'on' => $_[0],
676                    'log-level' => 'local7.debug',
677                    'dir' => 'out',
678                    'all' => 1,
679                    'active' => 1 };
680         if (@$rules) {
681                 &ipfilter::insert_rule($rule2, $rules->[0]);
682                 }
683         else {
684                 &ipfilter::create_rule($rule2);
685                 }
686         }
687
688 if (!$gotall) {
689         # Apply config
690         return &ipfilter::apply_configuration();
691         }
692 return undef;
693 }
694
695 # delete_ipfilter_rules()
696 # Delete the logging rules used for ipfilter
697 sub delete_ipfilter_rules
698 {
699 &foreign_require("ipfilter", "ipfilter-lib.pl");
700 local $rules = &ipfilter::get_config();
701 local $rule1 = &find_ipfilter_rule($rules, $config{'iface'}, "in");
702 local $rule2 = &find_ipfilter_rule($rules, $config{'iface'}, "out");
703 &ipfilter::delete_rule($rule1) if ($rule1);
704 &ipfilter::delete_rule($rule2) if ($rule2);
705 if ($rule1 || $rule2) {
706         return &ipfilter::apply_configuration();
707         }
708 return undef;
709 }
710
711 # find_ipfilter_rule(&rules, iface, dir)
712 sub find_ipfilter_rule
713 {
714 local ($rule) = grep { $_->{'action'} eq 'log' &&
715                        $_->{'on'} eq $_[1] &&
716                        $_->{'all'} &&
717                        $_->{'active'} &&
718                        $_->{'log-level'} eq "local7.debug" &&
719                        $_->{'dir'} eq $_[2] } @{$_[0]};
720 return $rule;
721 }
722
723 # process_ipfilter_line(line, &hours, time-now)
724 # Process a IPFilter firewall line, and returns 1 if successful
725 sub process_ipfilter_line
726 {
727 local ($line, $hours, $time_now) = @_;
728 if ($line =~ /^(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+).*ipmon\S*:.*\s$config{'iface'}\s+\S+\s+\S+\s+(\S+)\s+->\s+(\S+)\s+\S+\s+(\S+)\s+len\s+(\d+)\s+\(?(\d+)\)?.*(IN|OUT)/) {
729         # Found a valid line
730         local ($mon, $day, $hr, $min, $sec) = ($1, $2, $3, $4, $5);
731         local ($src, $dest, $proto, $len, $dir) = ($6, $7, uc($8), $10, lc($11));
732         local ($srchost, $srcport) = split(/,/, $src);
733         local ($desthost, $destport) = split(/,/, $dest);
734         $proto =~ s/:.*//;
735         local $len = 1024;
736
737         # Work out the real time
738         local $tm = timelocal($sec, $min, $hr, $day,
739                               &month_to_number($mon), $time_now[5]);
740         if ($tm > $time_now + 24*60*60) {
741                 # Was really last year
742                 $tm = timelocal($sec, $min, $hr, $day,
743                                 &month_to_number($mon), $time_now[5]-1);
744                 }
745         local $htm = int($tm/(60*60));
746
747         # Update the appropriate counters
748         local $hour = &get_hour($htm);
749         if (&indexof($hour, @$hours) < 0) {
750                 push(@$hours, $hour);
751                 }
752         local $port;
753         if ($proto eq 'TCP' || $proto eq 'UDP') {
754                 if ($dir eq "in") {
755                         $port = '_'.$destport.'_'.$srcport;
756                         }
757                 else {
758                         $port = '_'.$srcport.'_'.$destport;
759                         }
760                 }
761         local $host = $dir eq "in" ? $dest : $src;
762         local $key = $host.'_'.$proto.$port;
763         local ($in, $out) = split(/ /, $hour->{$key});
764         if ($dir eq "in") {
765                 $in += $len;
766                 }
767         else {
768                 $out += $len;
769                 }
770         $hour->{$key} = int($in)." ".int($out);
771         return 1;
772         }
773 else {
774         return 0;
775         }
776 }
777
778 # get_ipfilter_loglevel()
779 sub get_ipfilter_loglevel
780 {
781 return ( "local7.debug" );
782 }
783
784 1;
785