in reply to using perl for arp collection

You could get ARP info via Telnet or Web, but my preference is to get it via SNMP. Here is some code you can plagerize - lot of stuf you probably won't use, and you will need to remove database references, but it should get you a lot more than you wanted.

Sorry - Not the cleanest code -- I was still learning SNMP.

Concentrate on "sub ARPtable".

####################################################### ## Bridgemac.pl ## Queries the main routers for the ARP table ## Queries E7 and Vertical horizon switches for ## their Forwarding database (All Mac addresses). ## Stores info collected into the ARP table of the ## Hardware Database. ## The router ARP entries are queried for MAC <=> IP ## The Switch table gives Mac <=> Port/Switch info. ## All Equipment queries use SNMP. ## ## This is intended to run periodically as a scheduled ## job that updates the database. ## Database queries are done externally (Via web app) ## By Vijay Anand , March 2003 -Overhauled Sept2003 ########################################################### use strict; use Net::SNMP qw(:snmp); use DBInterface; # Vijay's homegrown DB module for Hardware Db my $session; my %SysInfo; # Contains {MaxPorts, manufacturer etc..}; my @FrontPorts; # Index by Maxports, gives last front-panel port numbe +r my @hosts=qw( ### Enter a list of Switch IP addresses here ......<<<<< ); ######################################### ### Program Starts ############# my $DB = DBInterface->new(); Query_Router_ARP_And_Update_DB('**IP address of ROUTER***'); foreach (@hosts){ Query_Switch_And_Update_DB($_); } # ARP tables may have changed or be incomplete # So run them again Query_Router_ARP_And_Update_DB('**IP address of ROUTER**'); print "--Closing DB--\n"; $DB = undef; # Close print "--end--\n"; ########################################### sub Query_Router_ARP_And_Update_DB{ &connect( host => shift, comm => shift || 'public'); my $ARPTable = ARPtable(); print "Got " . %{$ARPTable} . " arp entries from $session->{Hostname +}\n"; my %myrec; $DB->{REC} = \%myrec; ##$DB->verbose(1); foreach (sort keys %$ARPTable){ #print " $ARPTable->{$_} : $_\n"; $myrec{Mac} =$ARPTable->{$_}; $myrec{IP} = $_; $DB->Write_Rec(); } } ########################################### sub Query_Switch_And_Update_DB{ $session = undef; %SysInfo = {}; &connect( host => shift, comm => shift || 'public'); &Initial_Params; print "Front panel ports = $FrontPorts[$SysInfo{MaxPorts}]\n"; my ($fdb, $PortName) = &queryfdb(); # First, save this switch's Primary address my %myrec = ( Mac => $SysInfo{Mac} , IP => $SysInfo{Host} , User => '' , HostName =>$SysInfo{Host} , SwitchName => $SysInfo{Model} , SwitchPortIdx => 0, SwitchPortName => 'Internal', HostType => 'Switch' , Notes => $SysInfo{Manufacturer} , UpstreamID =>'' ); $DB->{REC} = \%myrec; my $SwitchRecId= $DB->Write_Rec(); for (my $port = 1;$port < @{$fdb}; $port++){ my $macs = $fdb->[$port] or next; $port > $FrontPorts[$SysInfo{MaxPorts}] and next; # Dont sho +w uplinks print "PORT $port $PortName->[$port] Macs= @$macs\n"; # $PortName->[$port] =~s/\s/_/g; # Db does not like spaces ? foreach (@$macs){ %myrec =( Mac=> $_, IP=> '' , User=> '' , HostName =>'' , SwitchName => $SysInfo{Host} , SwitchPortIdx => $port, ## SwitchPortName => $PortName->[$port], HostType => '' , Notes => '' , UpstreamID =>$SwitchRecId ); $DB->Write_Rec(); }#End For Each }#End For } # End Main Loop ################################################################# sub connect { my %cla = @_; $cla{comm} = "public" unless exists $cla{comm}; $session = Net::SNMP->session(Hostname => $cla{host}, Community => $cla{comm}); if (!defined($session)) { die "**SNMP Connect ERROR for %s: %s.\n",$_; } } ################################################################# sub Initial_Params{ my $sysUpTime ='1.3.6.1.2.1.1.3.0'; my $sysDescr =qq(1.3.6.1.2.1.1.1.0); #sy +stem.sysDescr.0 my $ifNumber = qq(1.3.6.1.2.1.2.1.0); my $dot1dStpTopChanges =qq(1.3.6.1.2.1.17.2.4.0); my $dot1dStpDesignatedRoot =qq(1.3.6.1.2.1.17.2.5.0); my $dot1dStpPortTable =qq(1.3.6.1.2.1.17.2.15); my $dot1dBaseNumPorts =qq(1.3.6.1.2.1.17.1.2.0); my $dot1dStpTimeSinceTopologyChange =qq(1.3.6.1.2.1.17.2.3.0); my $dot1dBaseBridgeAddress =qq(1.3.6.1.2.1.17.1.1.0); # Initialize Frontports to be the same as that reported for (my $i = 0; $i < 1024; $i++){ $FrontPorts[$i] = 999999; # View ALL ports, by default } # Now do the special cases .. $FrontPorts[26] = 24; # Vh2402 $FrontPorts[61] = 48; # 6h302-48 $FrontPorts[19] = 06; # 6G306-06 my $result = $session->get_request( -varbindlist => [$sysUpTime, $dot1dStpTopChanges, $sysD +escr, $dot1dStpDesignatedRoot, $dot1dBaseNumPo +rts, $dot1dStpTimeSinceTopologyChange, $dot1dBaseBridgeAddress,$ifNumber ]); if (!defined($result)) { die ("ERROR in initial request: %s.\n", $session->error); } #print( $session->hostname . " " . # &showinfo($session,"TC_count",$dot1dStpTopChanges) . # &showinfo($session,"rootBridge",$dot1dStpDesignatedRoot) +. # &showinfo($session,"ports",$dot1dBaseNumPorts) . # &showinfo($session,"TC_Time",$dot1dStpTimeSinceTopologyCh +ange) . # &showinfo($session,"This-BridgeMac", $dot1dBaseBridgeAddr +ess) . # &showinfo($session,"This-Bridge", $sysDescr) . # "\n" # ); $SysInfo{TCCount}= $session->var_bind_list()->{$dot1dStpTop +Changes}; $SysInfo{TCTime}= $session->var_bind_list()->{$dot1dStpTime +SinceTopologyChange}; $SysInfo{RootBridge} = $session->var_bind_list()->{$dot1dStpDes +ignatedRoot}; $SysInfo{RootBridge} =~ s/^0x//; # Zap the leading "0x" $SysInfo{MaxPorts}= $session->var_bind_list()->{$dot1dBaseNu +mPorts}; if ($session->var_bind_list()->{$ifNumber} != $SysInfo{MaxPorts +}){ print "**Problem: Number of Interfaces reported(" . $session->var_bind_list()->{$ifNumber} . ") Disagrees with number of Bridge ports($SysInfo{Ma +xPorts})\n" ; } $SysInfo{Mac}= $session->var_bind_list()->{$dot1dBaseBr +idgeAddress}; $SysInfo{Mac} =~ s/^0x//; # Zap the leading "0x" $SysInfo{Descr}=$session->var_bind_list()->{$sysDescr}; $SysInfo{Descr}=~ /([a-zA-Z ]+)\s.*\s([\w\-]*\d+[\w\-]*).*\s[V|R]E[V|R]\w*?\s +([\w\.\(\)]+)/iso; $SysInfo{Manufacturer} = $1; $SysInfo{Model} = $2; $SysInfo{Version} = $3; if (! $SysInfo{Manufacturer}){ # We did NOT get a Manufacturer - try to Re-parse for SSR, w +hich is like # 6SSRM/6C107 - Enterasys Networks Firmware Version: E9.0.7 +.0 PROM Version: prom- $SysInfo{Descr}=~m/(\S+)[\s-]+(\S+).*?\s[V|R]E[V|R][\w:]*?\s +([\w\.\(\)-]+)/iso; $SysInfo{Manufacturer} = $2; $SysInfo{Model} = $1; $SysInfo{Version} = $3; } $SysInfo{Host} = $session->hostname; # Vertical Horizon's dont report Manufacturer etc .. if ($SysInfo{Mac} =~m/^0001f4/ && $SysInfo{MaxPorts}== 26){ $SysInfo{Manufacturer} = "Enterasys (Guess based on Mac)"; $SysInfo{Model} = "VH2402"; $SysInfo{Version} = "No way of knowing"; } #SSR's report themselves differently if ($SysInfo{Descr} =~m/^6SSRM.*Version: (\w+)/){ $SysInfo{Manufacturer} = "Enterasys"; $SysInfo{Model} = "6SSRM/6C107"; $SysInfo{Version} = $1; } while (my ($k, $v) = each %SysInfo){ print " SYSTEM $k=$v;\n"; } } ################################################################# sub queryfdb { my ($key, $newkey); my @PortMacs; # An array of Array ref's my @PortDesc; # Description of each port my $dot1dBaseBridgeAddress = qq(1.3.6.1.2.1.17.1.1); my $dot1dBaseNumPorts = qq(1.3.6.1.2.1.17.1.2); my $dot1dTpFdbAddress = qq(1.3.6.1.2.1.17.4.3.1.1); my $dot1dTpFdbPort = qq(1.3.6.1.2.1.17.4.3.1.2); my $dot1dBasePortIfIndex = qq(1.3.6.1.2.1.17.1.4.1.2); my $ifName = qq(1.3.6.1.2.1.31.1.1.1.1); my $macref = $session->get_table($dot1dTpFdbAddress); $session->error and return {error => $session->error}; #foreach my $oid (oid_lex_sort(keys(%{$macref}))){ # print "OID $oid Mac " . $macref->{$oid} . "\n"; #} my $portref = $session->get_table($dot1dTpFdbPort); $session->error and return {error => $session->error}; my $portIfIndex = $session->get_table($dot1dBasePortIfIndex); $session->error and return {error => $session->error}; #foreach my $p (oid_lex_sort(keys(%{$portIfIndex}))){ # print " PORTIDX $p IFIDx " . $portIfIndex->{$p} . "\n"; #} my $PortNameRef = $session->get_table($ifName); $session->error and return {error => $session->error}; #foreach my $p (oid_lex_sort(keys(%{$PortNameRef}))){ # print " PORTNAME for Index $p Name=" . $PortNameRef->{$p} + . ";\n"; #} while (my($portkey,$port) = each %$portref) { ($newkey = $portkey) =~ s/$dot1dTpFdbPort\.//; # print "Pkey Port " . $portref->{$portkey} # #. " macKey $dot1dTpFdbAddress\.$newkey; Mac=" # . " mac " . $macref->{"$dot1dTpFdbAddress\.$newkey"} # . "\n"; $port > $FrontPorts[$SysInfo{MaxPorts}] and next; # Dont save +uplinks my $mac = $macref->{"$dot1dTpFdbAddress\.$newkey"}; length($mac) < 14 and next ; $mac =~ s/0x//; my $ifIndex = $portIfIndex->{$dot1dBasePortIfIndex . "\." . $p +ort}; ##$ifIndex == $port or print " IfIndex $ifIndex = Port $port + \n"; push @{$PortMacs[$ifIndex]}, $mac; $PortDesc[$ifIndex] =$PortNameRef->{$ifName . "\." . $ifIndex} +; } #print "**Port Count = " . scalar(%{$portref}) # . " Mac count = " . scalar(%{$macref}) # . " Portmac size=" . scalar(@PortMacs) # . "\n"; #for (my $i = 0; $i < @PortMacs; $i++) { # $PortMacs[$i] or next; # print "--Port $i MACS: @{$PortMacs[$i]} \n"; #} return (\@PortMacs, \@PortDesc); } ################################################################# sub showinfo(){ my ($session,$varname,$oid) = @_; my $format="%5d"; # Default format, if numeric my $val = $session->var_bind_list()->{$oid}; if (length($val)> 5 or $val > 9999){ return $varname . "=" . $val . "; "; } return $varname . "=" . sprintf("%6d", $val) . "; "; } ################################################################# ################################################################# sub ARPtable { my %MacbyIP; my $ifName = qq(1.3.6.1.2.1.31.1.1.1.1); my $ipNetToMediaNetAddress = qq(1.3.6.1.2.1.4.22.1.3); #Idx=Instanc +e, returns IP my $ipNetToMediaPhysAddress= qq(1.3.6.1.2.1.4.22.1.2); #Idx=Instanc +, Returns Mac my $ipNetToMediaIfIndex = qq(1.3.6.1.2.1.4.22.1.1); #Idx=Instanc +e, returns IFIdx my $ipNetToMediaEntry = qq(1.3.6.1.2.1.4.22.1); # Entire ARP t +able (Unused) my $ifIndex = qq(1.3.6.1.2.1.2.2.1.1); my $ifDescr = qq(1.3.6.1.2.1.2.2.1.2); my $ifPhysAddress = qq(1.3.6.1.2.1.2.2.1.6); my $IPref = &GetCleanTableRef($ipNetToMediaNetAddress); my $Macref= &GetCleanTableRef($ipNetToMediaPhysAddress, "^0x",""); # my $Portref=&GetCleanTableRef($ipNetToMediaIfIndex); # my $IfIdxref=&GetCleanTableRef($ifIndex); # my $IfDescref=&GetCleanTableRef($ifDescr); # my $IfEthAddrref=&GetCleanTableRef($ifPhysAddress); foreach my $idx (sort keys(%{$IPref})){ # my $desc = $IfDescref->{$IfIdxref->{$Portref->{$idx}}}; # $desc=~s/^IP interface: //; # print " Mac=" . $Macref->{$idx} . " len=" . length($Macref +->{$idx}) ##. " Eth=" . $IfEthAddrref->{$idx} # . " IP=" . $IPref->{$idx} # # . " Port=" . $Portref->{$idx} # # . " Desc=" . $desc # . "\n"; ##. " Idx=$idx\n"; if (length($Macref->{$idx}) > 11){ $MacbyIP{$IPref->{$idx}} = $Macref->{$idx}; } } return (\%MacbyIP); } ################################################################# sub GetCleanTableRef{ my $oid = shift; my ($subst_find, $subst_replacetext)=@_; my ($Tab, $NewTab, $newkey); $Tab = $session->get_table($oid); $session->error and die "Error getting $oid : " . $session->error; foreach my $idx (keys(%{$Tab})){ ($newkey = $idx) =~ s/$oid\.//; # print "OIDKey=$idx Info=" . $Tab->{$idx} . " New=$newkey\ +n"; $NewTab->{$newkey} = $Tab->{$idx}; $subst_find and $NewTab->{$newkey} =~s/$subst_find/$subst_r +eplacetext/; } return $NewTab; } #################################################################

    Earth first! (We'll rob the other planets later)

Replies are listed 'Best First'.
Re^2: using perl for arp collection
by Anonymous Monk on Nov 17, 2004 at 14:50 UTC
    Thanks everybody for you help. That code is exactly the kind of thing i was looking for. To Netwallah, it also sounds like the task for which it was used is similar to what i intend to do.This is the first time the task has been performed. Basically i am going to collect the arp table off our router and hopefully the switch forwarding tables off our hp switches. the output of both things were then going to be inserted into a databse (i have a script to put the output into an sql database) which would be periodically updated so as to give an idea of what is on our network at any given time. The database would have a record for each address with fields such as the related mac or ip address, the port that the mac address relates to( i forsee this could be difficult) and when it last appeared on the network. so to get to the point, how difficult would it be to change Netwallah's script to do all these things? I would also be interested in seeing what the DBinterface module does as this may be better then adding more code to put the resulting output into an sql database.Any ideas netwallah? sorry for the long winded post but i am very new to this perl malarky and am still trying to find my feet.Once again thanks for all the replies so far.
      I will be happy to share the Database structure, and related perl module with you - the Database is SQLServer, and the module handles the ODBC interface to a table named "ARP". I am not sure if this is appropriate for your environment.

      There is anothere script which scans the WINS database and populates device Names.

      Please /msg me with your e-mail address, so I can send it to you.

      Here is the layout of the ARP table:

          Earth first! (We'll rob the other planets later)