zeek/scripts/localnetMAC.pl.in

184 lines
5.9 KiB
Perl
Executable file

#!@PERL@
##This script assumes that there are a lot more external IP Adresses
##than internal ones. It associates all IP adresses with a MAC Adress
##and tracks what MAC adress communicates with what other MAC adress.
use strict;
use IP4;
use Getopt::Std;
my $usage="localnetMac.pl -r <dumpfile> or
localnetMac.pl -t <ascii file>
options:
\t-a <aggregate up to bits>
\t-b <output bro-syntax internal nets to file>
\t-m do not ignore multicast IP addresses
\t-v output debug info
\nInput is taken either from plain or gzip compressed files.
Input formats:
\tlibpcap dump file containing ethernet packets
\tasci file containing <LinkLayerAdr1 LinkLayerAdr2 IPAdr1 IPAdr2> per line
\nNote: for libpcap inputs currently only ethernet is supported. Other link layer protocols should work if using ascii input.\n";
my %args;
getopts("a:b:mr:t:v", \%args);
my $aggto=0;
my $broout="";
my $decomp;
my $multicast = 0;
my $MCASTMIN=224;
my $MCASTMAX=239;
my $debug = 0;
if (!defined $args{r} and !defined $args{t}){die $usage;}
if (defined $args{a}){$aggto = $args{a};}
if (defined $args{b}){$broout=$args{b};}
if (defined $args{m}){$multicast = 1;}
if (defined $args{v}){$debug = 1;}
if($args{r}=~/gz$/ or $args{t}=~/gz$/){
$decomp = `which zcat`;
chomp($decomp);
if ($decomp eq ""){
$decomp = `which gzcat`;
chomp($decomp);
}
if ($decomp eq ""){
die "You need zcat or gzcat in your \$PATH in order to process compressed files\n";
}
}
my $fh;
if ($args{r} and $args{r}=~/gz$/){
open (IN, "$decomp $args{r} |../aux/adtrace/adtrace -|") or die "cannot execute $decomp $args{r} |../aux/adtrace/adtrace - : $!\n";
$fh = *IN;
}elsif($args{r}){
open (IN, "../aux/adtrace/adtrace $args{r}|") or die "cannot execute ./adtrace/adtrace $args{r}: $!\n";
$fh = *IN;
}elsif($args{t} and $args{t}=~/gz$/){
open (IN, "$decomp $args{t} |") or die "cannot execute $decomp $args{t} | : $!\n";
$fh = *IN;
}elsif($args{t} and $args{t} eq "-"){
$fh = *STDIN;
}else{
open (IN, "$args{t}") or die "cannot open $args{t}: $!\n";
$fh = *IN;
}
my %cMacs;
my %macIP;
#for statistics:
my $ips=0;
my $pkt=0;
my $line;
while ($line=<$fh>){
chomp($line);
$pkt++;
my ($sMac, $dMac, $sIP, $dIP)=split(/ /, $line);
if (!$multicast and $sIP=~/^(\d+)\./ and $1>=$MCASTMIN and $1<=$MCASTMAX){next;}
if (!$multicast and $dIP=~/^(\d+)\./ and $1>=$MCASTMIN and $1<=$MCASTMAX){next;}
$macIP{$sMac}->{count}++ if (!exists $macIP{$sMac}->{$sIP});
$macIP{$sMac}->{$sIP}++;
$macIP{$dMac}->{count}++ if (!exists $macIP{$dMac}->{$dIP});
$macIP{$dMac}->{$dIP}++;
$cMacs{join(" ", sort($sMac, $dMac))}++;
}
close ($fh);
foreach my $mac (keys %macIP){
$ips += $macIP{$mac}->{count};
}
printf ("observed %d MAC adresses\n", scalar(keys %macIP));
print (join ("\n", keys %cMacs));
print "\n";
print "observed $pkt packets and $ips distinct IP adresses\nLocal IP addresses:\n";
if ($broout){
open (OUT, "> $broout") or die "cannot open $broout: $!\n";
print OUT "### Local Networks automatically generated by localnetMAC.pl ###\n";
if ($aggto){
print OUT "### NOTE: Internal Networks have been aggregated up to /$aggto networks.\n";
print OUT "### NOTE: Therefore it may happen that some external Networks\n";
print OUT "### NOTE: are considered local\n";
}
print OUT "### file generated at ".localtime()." (local system-time)\n";
printf OUT ("### observed %d MAC adresses:\n###\t", scalar(keys %macIP));
print OUT (join ("\n###\t", keys %cMacs));
print OUT "\n";
print OUT "### observed $pkt packets and $ips distinct IP adresses\n";
print OUT "\n\n";
print OUT "\@load site\n\n";
print OUT "redef local_nets: set[subnet] = {\n";
}
foreach my $macPair (keys %cMacs){
my ($mac1, $mac2) = split(/ /, $macPair);
my %record1;
my %record2;
my ($smallRec, $bigRec);
$record1{mac} = $mac1;
$record2{mac} = $mac2;
$record1{hash} = $macIP{$mac1};
$record2{hash} = $macIP{$mac2};
$record1{count} = delete $record1{hash}->{count};
$record2{count} = delete $record2{hash}->{count};
$record1{masks} = [];
$record2{masks} = [];
if ($debug){
print "*** $mac1 ($record1{count}) ***\n";
print join("\n", sort keys %{$macIP{$mac1}});
print "\n*** $mac1 ($record1{count}) end***\n";
print "*** $mac2 ($record2{count}) ***\n";
print join("\n", sort keys %{$macIP{$mac2}});
print "\n*** $mac2 ($record2{count}) end***\n";
}
my @ips1 = map(getIPFromString($_), keys %{$record1{hash}});
$record1{ips} = \@ips1;
aggregateSinglesTo($record1{ips}, $record1{masks}, $aggto) if ($aggto);
my @ips2 = map(getIPFromString($_), keys %{$record2{hash}} );
$record2{ips} = \@ips2;
aggregateSinglesTo($record2{ips}, $record2{masks}, $aggto) if ($aggto);
if (scalar( @{$record1{ips}} ) < scalar( @{$record2{ips}} )){
$smallRec = \%record1;
$bigRec = \%record2;
}else{
$smallRec = \%record2;
$bigRec = \%record1;
}
if ($broout){
printf OUT ("\t# $smallRec->{mac}: %d(%d) IPs (considered local);\n\t# $bigRec->{mac}: %d(%d) IPs (considered extern)\n",
scalar( @{$smallRec->{ips}} ),$smallRec->{count},
scalar( @{$bigRec->{ips}} ), $bigRec->{count});
}
printf ("$smallRec->{mac}: %d(%d) IPs (considered local); $bigRec->{mac}: %d(%d) IPs (considered extern)\n",
scalar( @{$smallRec->{ips}} ),$smallRec->{count},
scalar( @{$bigRec->{ips}} ), $bigRec->{count});
@{$smallRec->{ips}} = map( getStringFromIP($_), @{$smallRec->{ips}} );
@{$smallRec->{masks}} = map( getPrefixFromMask($_), @{$smallRec->{masks}} );
for(my $i = 0; $i <= $#{$smallRec->{ips}}; $i++){
if ($smallRec->{masks}->[$i]){
print "$smallRec->{ips}->[$i]/$smallRec->{masks}->[$i]\n";
if ($broout){print OUT "\t $smallRec->{ips}->[$i]/$smallRec->{masks}->[$i],\n";}
}else{
print "$smallRec->{ips}->[$i]\n";
if ($broout){print OUT "\t $smallRec->{ips}->[$i]/32,\n";}
}
}
}
if ($broout){
print OUT "};\n";
close(OUT);
}