zeek/scripts/IP4.pm

150 lines
3.5 KiB
Perl

package IP4;
use Exporter;
@ISA = ('Exporter');
@EXPORT = ( 'getIPFromString',
'getStringFromIP',
'getMaskFromPrefix',
'getPrefixFromMask',
'isPartOf',
'aggregateSinglesTo'
);
use strict;
my $DEBUG = 0;
sub getIPFromString{
my ($net) = @_;
my @octets = split (/\./, $net);
#check ip!
foreach my $oct (@octets){
if ($oct!~/\d+/ || $oct<0 || $oct > 255){return 0;}
}
my $ip=0;
for (my $i = 0; $i < 4; $i++){
$ip |= $octets[$i] << ((3-$i)*8);
}
return $ip;
}
sub getStringFromIP{
my ($net) = @_;
my @octets;
my $bitmask=0xff;
for (my $i = 0; $i<4; $i++){
$octets[$i] = ($net & $bitmask);
$net >>= 8;
}
return "$octets[3].$octets[2].$octets[1].$octets[0]";
}
sub getMaskFromPrefix{
my ($pre) = @_;
#check prefix!
if ($pre!~/\d+/ || $pre < 0 || $pre > 32){return 0;}
my $mask=0;
for (my $i = 0; $i < $pre; $i++){
$mask |= 1 << (31-$i);
}
return $mask;
}
sub getPrefixFromMask{
my ($mask) = @_;
if ($mask == 0){return 0}; #special case, we would loop forever with this:
my $prefix;
for ($prefix = 32; !($mask & 1); $prefix--){
$mask >>= 1;
}
return $prefix;
}
sub isPartOf{
my ($iip, $imask, $oip, $omask) = @_;
if ($omask > $imask){return 0;}
#if the net which should contain the other is
#smaller we did something wrong!
return ( (($oip ^ $iip) & $omask) == 0 );
}
sub aggregateSinglesTo{
#paramters:
#1. reference to array of addresses (will be changed!)
#2. refernce to array of masks (will be deleted and changed)
#3. max Bits to aggregate to.
my ($addr, $masks, $bitlimit) = @_;
$bitlimit = 32-$bitlimit; #the way it will be used we'll need the inverse
@$addr = sort{$a<=>$b}(@$addr) or return 0;
@$masks = ();
my $fullmask = getMaskFromPrefix(32);
foreach my $dummy (@$addr){push(@$masks, $fullmask);}
if ($DEBUG){
print STDERR "sorted list before aggregating\n";
print STDERR join(" ", map(getStringFromIP($_), @$addr));
print STDERR "\n";
}
for (my $i = 0;
$i < (scalar(@$addr) - 1);
$i ++)
{
my $lip = $addr->[$i];
my $lmask = $masks->[$i];
my $hip = $addr->[$i + 1];
my $hmask = $masks->[$i + 1];
if (isPartOf($hip, $hmask, $lip, $lmask)) { #parameter: (inner, outer)
if ($DEBUG){
printf STDERR ("removing %s/%s since it is contained in %s/%s ",
getStringFromIP($hip), getPrefixFromMask($hmask),
getStringFromIP($lip), getPrefixFromMask($lmask) );
}
splice(@$addr, $i + 1, 1);
splice(@$masks, $i + 1, 1);
-- $i;
}else{
my $nb = $lip;
$nb ^= $hip; #look for first non-matching bit!
my $firstdiff=0;
while ($nb > 0){
$firstdiff++;
$nb >>= 1;
}
if ($firstdiff <= $bitlimit){
if ($DEBUG){print STDERR "$firstdiff : ";}
while($firstdiff>0){
$firstdiff--;
$nb <<= 1;
$nb += 1;
}
my $nm = ~$nb; #negate to get the new (joint) mask
my $na = $lip & $nm;
$addr->[$i] = $na;
$masks->[$i] = $nm;
if ($DEBUG){
printf STDERR ("%s to %s/%s (aggregating %s)\n",
getStringFromIP($lip), getStringFromIP($addr->[$i]),
getPrefixFromMask($masks->[$i]), getStringFromIP($hip));
}
splice(@$addr, $i + 1, 1);
$i--; #do with the same address again. perhaps it collects even more
}
}
}
if ($DEBUG){
print STDERR "sorted list after aggregation\n";
print STDERR join(" ", map(getStringFromIP($_), @$addr));
print STDERR "\n";
}
return 1;
}
1;