1#!/usr/local/bin/perl 2# 3# 4use strict; 5use lib "<<Make:LIB>>"; 6use Netdot::Model; 7use Getopt::Long qw(:config no_ignore_case bundling); 8use Log::Log4perl::Level; 9 10my %self; 11$self{ARP_LIMIT} = 0; 12$self{FWT_LIMIT} = 0; 13 14my $USAGE = <<EOF; 15 16 Locate a device given its name, MAC or IP address. 17 18 By default, this script uses information from the Netdot database. 19 The user also has the option of doing a "live" search by querying 20 relevant devices in the network. In this case, providing a VLAN id 21 can significantly speed up the search. 22 23 Usage: $0 [options] <ether|ip|name> 24 25 Available options: 26 27 -A|--arp_limit <value> Number of latest ARP cache entries to show (default: $self{ARP_LIMIT}) 28 -F|--fwt_limit <valud> Number of latest Forwarding Table entries to show (default: $self{FWT_LIMIT}) 29 -v|--vlan <vlanid> VLAN id to use when searching addresses "live" 30 -f|--forcelive Force a "live" search 31 -d|--debug Show debugging output 32 -h|--help Show help 33 34EOF 35 36my $MAC = Netdot->get_mac_regex(); 37 38# handle cmdline args 39my $result = GetOptions( "A|arp_limit:s" => \$self{ARP_LIMIT}, 40 "F|fwt_limit:s" => \$self{FWT_LIMIT}, 41 "v|vlan:s" => \$self{VLAN}, 42 "f|forcelive" => \$self{FORCE_LIVE}, 43 "h|help" => \$self{HELP}, 44 "d|debug" => \$self{DEBUG}, 45 ); 46 47my $address = shift @ARGV; 48 49if ( !$result ) { 50 print $USAGE; 51 die "Error: Problem with cmdline args\n"; 52} 53if ( $self{HELP} ) { 54 print $USAGE; 55 exit; 56} 57if ( !$address ) { 58 print $USAGE; 59 die "Error: Missing address or name\n"; 60} 61 62my $logger = Netdot->log->get_logger('Netdot::Model::Device'); 63my $logscr = Netdot::Util::Log->new_appender('Screen', stderr=>0); 64$logger->add_appender($logscr); 65 66# Notice that $DEBUG is imported from Log::Log4perl 67$logger->level($DEBUG) if ( $self{DEBUG} ); 68 69print "--------------------\n"; 70 71if ( $address =~ /$MAC/ ){ 72 $address = PhysAddr->format_address($address); 73 if ( $self{FORCE_LIVE} ){ 74 &search_live(mac=>$address, vlan=>$self{VLAN}); 75 }else{ 76 &show_mac($address, 1); 77 } 78 79}elsif ( Ipblock->matches_ip($address) ){ 80 81 if ( $self{FORCE_LIVE} ){ 82 &search_live(ip=>$address, vlan=>$self{VLAN}); 83 }else{ 84 &show_ip($address, 1); 85 } 86}else{ 87 # Try to resolve 88 if ( my @ips = Netdot->dns->resolve_name($address) ){ 89 foreach my $ip ( @ips ){ 90 if ( $self{FORCE_LIVE} ){ 91 &search_live(ip=>$ip, vlan=>$self{VLAN}); 92 }else{ 93 &show_ip($ip, 1); 94 } 95 } 96 }else{ 97 die "$address not found\n" 98 } 99} 100 101############################################################################### 102# 103# Subroutine Section 104# 105############################################################################### 106 107############################################################################### 108sub show_ip { 109 my ($address, $show_arp) = @_; 110 my $ip = Ipblock->search(address=>$address)->first; 111 my $subnet; 112 if ( $ip ){ 113 my $parent = $ip->parent; 114 if ( int($parent->status) && $parent->status->name eq "Subnet" ){ 115 $subnet = $parent; 116 } 117 print "\n"; 118 print "IP Address : ", $address, "\n"; 119 if ( $subnet ){ 120 print "Subnet : ", $subnet->get_label, ", ", $subnet->description, "\n"; 121 } 122 if ( my $name = Netdot->dns->resolve_ip($address) ){ 123 print "DNS : ", $name, "\n"; 124 } 125 if ( $show_arp ){ 126 my $last_n = $self{ARP_LIMIT} || 1; 127 if ( my $arp = $ip->get_last_n_arp($last_n) ){ 128 my @rows; 129 my %tstamps; 130 my $latest_mac; 131 foreach my $row ( @$arp ){ 132 my ($iid, $macid, $tstamp) = @$row; 133 my $lbl = Interface->retrieve($iid)->get_label; 134 push @{$tstamps{$tstamp}{$macid}}, $lbl; 135 } 136 if ( $self{ARP_LIMIT} ){ 137 print "\nLatest ARP cache entries:\n\n"; 138 } 139 foreach my $tstamp ( reverse sort keys %tstamps ){ 140 foreach my $macid ( keys %{$tstamps{$tstamp}} ){ 141 my $mac = PhysAddr->retrieve($macid)->address; 142 $latest_mac = $mac unless defined $latest_mac; 143 if ( $self{ARP_LIMIT} ){ 144 print $tstamp, " ", $mac, " ", (join ', ', @{$tstamps{$tstamp}{$macid}}), "\n"; 145 } 146 } 147 } 148 &show_mac($latest_mac); 149 } 150 } 151 }else{ 152 warn "$address not found in DB. Try searching live (--forcelive)\n"; 153 exit 0; 154 } 155} 156 157 158############################################################################### 159sub show_mac { 160 my ($address, $show_arp) = @_; 161 162 my $mac = PhysAddr->search(address=>$address)->first; 163 if ( !$mac ){ 164 warn "$address not found in DB. Try searching live (--forcelive)\n"; 165 exit 0; 166 } 167 168 print "\n"; 169 print "MAC Address : ", $mac->address, "\n"; 170 print "Vendor : ", $mac->vendor, "\n"; 171 print "First Seen : ", $mac->first_seen, "\n"; 172 print "Last Seen : ", $mac->last_seen, "\n"; 173 174 my $last_n_fte = $self{FWT_LIMIT} || 1; 175 my $last_n_arp = $self{ARP_LIMIT} || 1; 176 177 my $fwt = $mac->get_last_n_fte($last_n_fte); 178 my $arp = $mac->get_last_n_arp($last_n_arp); 179 my @devices = $mac->devices; 180 if ( @devices ){ 181 print "\nDevices using this address: "; 182 print join(', ', map { $_->get_label } @devices), "\n"; 183 } 184 my @interfaces = $mac->interfaces; 185 if ( @interfaces ){ 186 print "\nInterfaces using this address: "; 187 print join(', ', map { $_->get_label } @interfaces), "\n"; 188 } 189 190 if ( $self{FWT_LIMIT} ){ 191 if ( $fwt && scalar @$fwt ){ 192 my %tstamps; 193 foreach my $row ( @$fwt ){ 194 my ($tstamp, $iid) = @$row; 195 my $iface = Interface->retrieve($iid); 196 my $lbl = $iface->get_label; 197 push @{$tstamps{$tstamp}}, $lbl; 198 } 199 200 print "\nLatest forwarding table entries:\n\n"; 201 202 foreach my $tstamp ( reverse sort keys %tstamps ){ 203 print $tstamp, ", ", join ', ', @{$tstamps{$tstamp}}, "\n"; 204 } 205 } 206 } 207 208 my ($latest_ip_id, $latest_ip); 209 if ( $show_arp ){ 210 if ( $self{ARP_LIMIT} ){ 211 print "\nLatest ARP cache entries:\n\n"; 212 } 213 214 if ( $arp && scalar @$arp ){ 215 my %tstamps; 216 foreach my $row ( @$arp ){ 217 my ($iid, $ipid, $tstamp) = @$row; 218 $latest_ip_id = $ipid unless $latest_ip_id; 219 my $lbl = Interface->retrieve($iid)->get_label; 220 push @{$tstamps{$tstamp}{$ipid}}, $lbl; 221 } 222 if ( $self{ARP_LIMIT} ){ 223 foreach my $tstamp ( reverse sort keys %tstamps ){ 224 foreach my $ipid ( keys %{$tstamps{$tstamp}} ){ 225 my $iplbl = Ipblock->retrieve($ipid)->get_label; 226 print $tstamp, ", ", $iplbl, ", ", (join ', ', @{$tstamps{$tstamp}{$ipid}}), "\n"; 227 } 228 } 229 } 230 $latest_ip = Ipblock->retrieve($latest_ip_id)->address; 231 } 232 } 233 234 &show_ip($latest_ip) if $latest_ip; 235 236 if ( scalar(@interfaces) == 1 && int($interfaces[0]->neighbor) ){ 237 print "\nNeighbor interface: ", $interfaces[0]->neighbor->get_label, "\n"; 238 }else{ 239 my $edge_port = $mac->find_edge_port(); 240 &print_location($edge_port) if $edge_port; 241 } 242} 243 244############################################################################### 245sub search_live{ 246 my (%argv) = @_; 247 248 my $info = Device->search_address_live(%argv); 249 if ( $info ){ 250 my ($ipaddr, $macaddr); 251 $ipaddr = $info->{ip}; 252 $macaddr = $info->{mac}; 253 if ( scalar keys %{$info->{routerports}} ){ 254 if ( $self{ARP_LIMIT} ){ 255 print "\nARP entries: \n"; 256 } 257 foreach my $id ( keys %{$info->{routerports}} ){ 258 my $iface = Interface->retrieve($id); 259 my $ip = (keys %{$info->{routerports}{$id}})[0]; 260 $ipaddr = $ip unless $ipaddr; 261 my $mac = $info->{routerports}{$id}{$ip}; 262 $macaddr = $mac unless $macaddr; 263 if ( $self{ARP_LIMIT} ){ 264 print $iface->get_label, ", ", $ip, ", ", $mac, "\n"; 265 } 266 } 267 } 268 if ( scalar keys %{$info->{switchports}} && $self{FWT_LIMIT} ){ 269 print "\nFWT entries: \n"; 270 foreach my $id ( keys %{$info->{switchports}} ){ 271 my $iface = Interface->retrieve($id); 272 print $iface->get_label; 273 print " "; 274 } 275 print "\n"; 276 } 277 print "\n"; 278 if ( $macaddr ){ 279 print "MAC Address : ", $macaddr, "\n"; 280 print "Vendor : ", $info->{vendor}, "\n" if $info->{vendor}; 281 } 282 if ( $ipaddr ){ 283 print "IP Address : ", $ipaddr, "\n"; 284 print "DNS : ", $info->{dns}, "\n" if $info->{dns}; 285 } 286 287 print "\n"; 288 &print_location($info->{edge}) if $info->{edge}; 289 290 }else{ 291 die "$address not found\n"; 292 } 293} 294 295############################################################################### 296sub print_location{ 297 my $port = shift; 298 return unless $port; 299 my $iface = Interface->retrieve($port); 300 return unless $iface; 301 my $info = ''; 302 $info .= ', '.$iface->description if $iface->description; 303 my $jack = ($iface->jack)? $iface->jack->get_label : $iface->jack_char; 304 $info .= ', '.$jack if $jack; 305 print "\nLocation : ", $iface->get_label, $info; 306 print "\nModel : ", $iface->device->product->get_label; 307 print "\n\n"; 308} 309 310=head1 AUTHOR 311 312Carlos Vicente, C<< <cvicente at ns.uoregon.edu> >> 313 314=head1 COPYRIGHT & LICENSE 315 316Copyright 2009 University of Oregon, all rights reserved. 317 318This program is free software; you can redistribute it and/or modify 319it under the terms of the GNU General Public License as published by 320the Free Software Foundation; either version 2 of the License, or 321(at your option) any later version. 322 323This program is distributed in the hope that it will be useful, but 324WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY 325or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 326License for more details. 327 328You should have received a copy of the GNU General Public License 329along with this program; if not, write to the Free Software Foundation, 330Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 331 332=cut 333