#!@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 or localnetMac.pl -t options: \t-a \t-b \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 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); }