Actions

Cisco MAC Comparison

From Mike Beane's Blog

What?

Recently I had to start a process to map PCs to ports on Cisco switches. While I don't have access to the Cisco gear directly, I can request dumps of the mac and arp tables. With that, I wrote some PERL to cross match based on MAC addresses.

I have newer code that not only does DNS lookup, but WINS as well for better resolution. The newer code is also less specific on the format of the switch in data.

Perl

Switch Input

#!/usr/bin/perl

opendir(DIR, "./switch_in");
my @files = grep(/\.txt$/,readdir(DIR));
closedir(DIR);

my $out="routerinputcommands.txt";
my $COMBINED="scannedswitches.csv";
my $LOGFILE_IN=();

my $SWITCHNAME=();
my $MACADDRESS=();
my $VLAN=();
my $CONNECTIONTYPE=();
my $PORTS=();

open OUT, ">$out" or die "Cannot open $out for write :$!";
open COMBINED, ">$COMBINED" or die "Cannot open $COMBINED for write :$!";

foreach $file (@files) 
    {
	$LOGFILE_IN="switch_in/$file";
	
	open LOGFILE_IN, $LOGFILE_IN or die "Cannot open $LOGFILE_IN for read :$!";


	
    while (<LOGFILE_IN>) 
	    {
        #Get Switch Name
        #sh mac-address-table | inc Fa0/
        #sh mac-address-table | inc Fa0/
        if ($_=~m/(.+)#sh mac(.+)/)
            {
            $SWITCHNAME=$1;
            print "Reading from Switch $1\n";
            }
        #ES403(SVH) (enable) sh cam dy | ex 2/49  
        
        elsif ($_=~m/(.+) \(enable\) (.+)/)
            {
            $SWITCHNAME=$1;
            print "Reading from Switch $1\n";
            }
        elsif ($_=~m/-------------------------------------------/)
            {}
        elsif ($_=~m/----    -----------       --------    -----/)
            {}
        elsif ($_=~m/Vlan    Mac Address       Type        Ports/)
            {}
        elsif ($_=~m/Total Mac Addresses for this criterion:/)
            {}

        elsif ($_=~m/Total Matching CAM Entries Displayed/)
            {}
        elsif ($_=~m/----  ------------------    -----  -------------------------------------------/)
            {}
        elsif ($_=~m/          Mac Address Table/)
            {}
        elsif ($_=~m/\* \= Static Entry. \+ \= Permanent Entry. \# \= System Entry. R \= Router Entry./)
            {}
        elsif ($_=~m/X \= Port Security Entry \$ \= Dot1x Security Entry/)
            {}
        elsif ($_=~m/VLAN  Dest MAC\/Route Des    \[CoS\]  Destination Ports or VCs \/ \[Protocol Type\]/)
            {}


        
        ######################################################################
        ####################sh mac-address-table | inc Fa0/ output############
        ######################################################################
        #Get specific line from sh mac-address-table | inc Fa0/ output
        #    1    0007.e95a.57e8    DYNAMIC     Fa0/2
        elsif ($_=~m/^(.+)    ([0-9a-f][0-9a-f][0-9a-f][0-9a-f]).([0-9a-f][0-9a-f][0-9a-f][0-9a-f]).([0-9a-f][0-9a-f][0-9a-f][0-9a-f])    (DYNAMIC)     (.*)/)
            {
            $MACADDRESS="$2.$3.$4";
            print OUT "sh arp \| inc $MACADDRESS\n";
            $VLAN=$1;
            $CONNECTIONTYPE=$5;
            $PORTS=$6;
            print COMBINED "$SWITCHNAME,$MACADDRESS,$CONNECTIONTYPE,$VLAN,$PORTS\n";
            }
        ######################################################################
        #####################show mac-address-table###########################
        ######################################################################
        # All    0100.0cdd.dddd    STATIC      CPU
        #   1    000f.1fd3.d85a    DYNAMIC     Fa0/14
        elsif ($_=~m/^(.+)    ([0-9a-f][0-9a-f][0-9a-f][0-9a-f]).([0-9a-f][0-9a-f][0-9a-f][0-9a-f]).([0-9a-f][0-9a-f][0-9a-f][0-9a-f])    (STATIC)    (.*)/)
            {
            $MACADDRESS="$2.$3.$4";
            print OUT "sh arp \| inc $MACADDRESS\n";
            $VLAN=$1;
            $CONNECTIONTYPE=$5;
            $PORTS=$6;
            print COMBINED "$SWITCHNAME,$MACADDRESS,$CONNECTIONTYPE,$VLAN,$PORTS\n";
            }
        
        #### VLAN  Dest MAC/Route Des    [CoS]  Destination Ports or VCs / [Protocol Type] 
        ###----  ------------------    -----  -------------------------------------------
        ##1     00-01-e6-4b-59-d1             2/7 [ALL]
        
        elsif ($_=~m/^(.+) ([0-9a-f][0-9a-f])-([0-9a-f][0-9a-f])-([0-9a-f][0-9a-f])-([0-9a-f][0-9a-f])-([0-9a-f][0-9a-f])-([0-9a-f][0-9a-f]) (.*) (.*)\/(.*) (.*)/)
            {
            $MACADDRESS="$2$3.$4$5.$6$7";
            print OUT "sh arp \| inc $MACADDRESS\n";
            $VLAN=$1;
            $CONNECTIONTYPE="N/A";
            my $tmpfa=$9;
            if ($tmpfa==2)
                {
                $tmpfa=0;
                }
            $PORTS="Fa$tmpfa/$10";
            #print "$_\n";   
            print COMBINED "$SWITCHNAME,$MACADDRESS,$CONNECTIONTYPE,$VLAN,$PORTS\n";
            }
        
        else {print "$_\n";}
        }
    }
>

Router Input

#!/usr/bin/perl

#### GET SWITCH INFO #####

my $SWITCH_IN="scannedswitches.csv";
my $out="scannedrouter.csv";
my $checkip=();
my $INFO="Matched Output.csv";
my $DEVICENAME=();

open SWITCH_IN, $SWITCH_IN or die "Cannot open $SWITCH_IN for read :$!";

while(<SWITCH_IN>)
   		{
        $temp=$_;
        chop ($temp);                #REMOVE ANY NEWLINE
        @SWITCH_ALL= split(/,/, $temp);   #Split
        
        push @SWITCH_NAME, $SWITCH_ALL[0];
        push @SWITCH_MACADDRESS, $SWITCH_ALL[1];
        push @SWITCH_TYPE, $SWITCH_ALL[2];
        push @SWITCH_VLAN, $SWITCH_ALL[3];
        push @SWITCH_PORT, $SWITCH_ALL[4];
        
        $SWITCH_TOTAL++;                
        }
close SWITCH_IN;

opendir(DIR, "./router_in");
my @files = grep(/\.txt$/,readdir(DIR));
closedir(DIR);

my $LOGFILE_IN=();

my $ROUTERNAME=();
my $ROUTERCONNECTION_TYPE=();
my $ROUTER_MACADDRESS=();
my $ROUTER_TIME=();
my $ROUTER_ARPA=();
my $ROUTER_IP=();
my $ROUTER_PORTSETTING=();

print "_________________________________________\n\n";
print "Reading in Router Data\n";
print "_________________________________________\n\n\n";

open OUT, ">$out" or die "Cannot open $out for write :$!";

foreach $file (@files) 
    {
	$LOGFILE_IN="router_in/$file";
	
	open LOGFILE_IN, $LOGFILE_IN or die "Cannot open $LOGFILE_IN for read :$!";


	
    while (<LOGFILE_IN>) 
	    {
        #Get Router Name
        #RA80(SVH)#sh arp | inc 0000.f843.6c6f 
        if ($_=~m/(.+)#(.+)/)
            {
            $ROUTERNAME=$1;
            }
        ######################################################################
        ####################sh arp ############
        ######################################################################
        #Get specific line from sh arp output
        #Internet  192.168.111.119         3   0007.e95a.57e8  ARPA   FastEthernet0/0
        #Internet  192.168.117.1           -   000f.2405.d141  ARPA   FastEthernet0/1.117
#^(.*)  ([0-9][0-9][0-9]).([0-9][0-9][0-9]).([0-9][0-9][0-9]).([0-9][0-9][0-9])        (\d*)   ([0-9a-f][0-9a-f][0-9a-f][0-9a-f]).([0-9a-f][0-9a-f][0-9a-f][0-9a-f]).([0-9a-f][0-9a-f][0-9a-f][0-9a-f])  (.*)   (.*)
        
        
        elsif ($_=~m/^(.*)  (\d+)\.(\d+)\.(\d+)\.(\d+)  (.*)   ([0-9a-f][0-9a-f][0-9a-f][0-9a-f]).([0-9a-f][0-9a-f][0-9a-f][0-9a-f]).([0-9a-f][0-9a-f][0-9a-f][0-9a-f])(.*) (.+)/)
            {
            $ROUTERCONNECTION_TYPE=$1;
            $ROUTER_IP="$2.$3.$4.$5";
            $ROUTER_MACADDRESS="$7.$8.$9";
            $ROUTER_TIME=$6;
            $ROUTER_ARPA=$11;
            $ROUTER_PORTSETTING=$10;
            print OUT "$ROUTERNAME,$ROUTERCONNECTION_TYPE,$ROUTER_IP,$ROUTER_MACADDRESS,$ROUTER_TIME,$ROUTER_ARPA,$ROUTER_PORTSETTING\n";
            
            push @ROUTER_NAME, $ROUTERNAME;
            push @ROUTER_CONN, $ROUTERCONNECTION_TYPE;
            push @ROUTER_IPADDRESS, $ROUTER_IP;
            push @ROUTER_MAC, $ROUTER_MACADDRESS;
            push @ROUTER_UPTIME, $ROUTER_TIME;
            push @ROUTER_ARPALIST, $ROUTER_ARPA;
            push @ROUTER_PORT, $ROUTER_PORTSETTING;

            
            }
        else
        {print"$_\n";}
            


        }
    }
    
########## DONE GATHERING INFORMATION ##########
################################################

########## Start Cross Matchnig ################

print "_________________________________________\n\n";
print "Cross Matching Router Data & Resolving DNS\n";
print "_________________________________________\n\n\n";

### ELEMENTS ###

        # @SWITCH_NAME
        # @SWITCH_MACADDRESS
        # @SWITCH_TYPE
        # @SWITCH_VLAN
        # @SWITCH_PORT

        # @ROUTER_NAME
        # @ROUTER_CONN
        # @ROUTER_IPADDRESS
        # @ROUTER_MAC
        # @ROUTER_UPTIME
        # @ROUTER_ARPALIST
        # @ROUTER_PORT
        
open INFO, ">$INFO" or die "Cannot open $INFO for write :$!";

print INFO "SWITCH_NAME,SWITCH_PORT,DEVICENAME,ROUTER_IPADDRESS,SWITCH_MACADDRESS,ROUTER_MAC,SWITCH_TYPE,SWITCH_VLAN,ROUTER_UPTIME,ROUTER_CONN\n";


my $SC=0;  #switch count

        foreach $SWITCH_MACADDRESS (@SWITCH_MACADDRESS)
            {
            $ARRAYSIZE = scalar (@ROUTER_MAC); 
           
            for ($i=0; $i < $ARRAYSIZE; $i++)
                {
                if ($ROUTER_MAC[$i] eq $SWITCH_MACADDRESS)
                    {
                    #print "$SWITCH_MACADDRESS = $ROUTER_MAC[$i]\n";
                    $checkip=$ROUTER_IPADDRESS[$i];
                    $DEVICENAME="";
                    &IPLOOKUP($checkip);
                    print INFO "$SWITCH_NAME[$SC],$SWITCH_PORT[$SC],$DEVICENAME,$ROUTER_IPADDRESS[$i],$SWITCH_MACADDRESS[$SC],$ROUTER_MAC[$i],$SWITCH_TYPE[$SC],$SWITCH_VLAN[$SC],$ROUTER_UPTIME[$I],$ROUTER_CONN[$I]\n";
                    
                    last; #Break from the loop
                    }
                }
                $SC++;
            }
            
            
            

#########

sub IPLOOKUP
    {
    use Net::DNS;
    use NetAddr::IP;
        
    my $ip = new NetAddr::IP (shift) || die "Unable to create NetAddr::IP object\n";
    my $res = Net::DNS::Resolver->new;
    my $num = $ip->num();
        
    for (my $i=0; $i<=$num; ++$i) {
        
    my $ip_address = $checkip;
        
          if ($ip_address) 
            {
            my $query = $res->search("$ip_address");
            if ($query) 
                {
                foreach my $rr ($query->answer) 
                    {
                    next unless $rr->type eq "PTR";
                    #print "$ip_address,",$rr->ptrdname, "\n";
                    $DEVICENAME=$rr->ptrdname;
                    }
                } 
            else 
                {
                #print "$ip_address,",$res->errorstring,"\n";
                }
          }
        
          ++$ip;
        
        }
    }