use Socket;
use Net::CIDR;
use Net::RawIP;
+use Time::HiRes;
require './access_list.pl';
require './nets.pl';
require './mbd.pm';
return $bits;
}
+# used for rate limiting
+my %last_sent = ();
+
+my %cidrcache = ();
+sub cache_cidrlookup {
+ my ($addr, $net) = @_;
+ my $key = $addr . " " . $net;
+
+ if (!exists($cidrcache{$key})) {
+ $cidrcache{$key} = Net::CIDR::cidrlookup($addr, $net);
+ }
+ return $cidrcache{$key};
+}
+
+my %rangecache = ();
+sub cache_cidrrange {
+ my ($net) = @_;
+
+ if (!exists($rangecache{$net})) {
+ ($rangecache{$net}) = Net::CIDR::cidr2range($net);
+ }
+
+ return $rangecache{$net};
+}
+
open LOG, ">>", "mbd.log";
my @ports = mbd::find_all_ports();
my ($dport, $daddr) = sockaddr_in(getsockname($sock));
my $size = length($data);
+ # Rate limiting
+ my $now = [Time::HiRes::gettimeofday];
+ if (exists($last_sent{$saddr}{$dport})) {
+ my $elapsed = Time::HiRes::tv_interval($last_sent{$saddr}{$dport}, $now);
+ if ($elapsed < 1.0) {
+ print LOG "$dport $size 2\n";
+ print inet_ntoa($saddr), ", $dport, $size bytes => rate-limited ($elapsed secs since last)\n";
+ }
+ next;
+ }
+
# We don't get the packet's destination address, but I guess this should do...
# Check against the ACL.
my $pass = 0;
print LOG "$dport $size $pass\n";
if (!$pass) {
- print "$dport, $size bytes => filtered\n";
+ print inet_ntoa($saddr), ", $dport, $size bytes => filtered\n";
+ next;
}
- next unless $pass;
+ $last_sent{$saddr}{$dport} = $now;
+
+ my $num_nets = 0;
for my $net (@Config::networks) {
- next if (Net::CIDR::cidrlookup(inet_ntoa($saddr), $net));
+ next if (cache_cidrlookup(inet_ntoa($saddr), $net));
- my ($range) = Net::CIDR::cidr2range($net);
+ my ($range) = cache_cidrrange($net);
$range =~ /-(.*?)$/;
my $broadcast = $1;
- print inet_ntoa($saddr), ", $dport, $size bytes => $broadcast\n";
-
$sendsock->set({
ip => {
saddr => inet_ntoa($saddr),
}
});
$sendsock->send;
+ ++$num_nets;
}
+ print inet_ntoa($saddr), ", $dport, $size bytes => ($num_nets networks)\n";
}
}