#!/usr/local/bin/perl
#
# dump bridge tables for a list of switches
#
# $Id: btdump,v 1.10 1999/06/04 01:46:07 trockij Exp $
#
# Copyright (C) 1998 Jim Trocki
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
use A3Com::BridgeTable;
use A3Com::ARP;
use A3Com::EtherDetail;
use Getopt::Std;
use Socket;

sub usage {
    print STDERR "$0: @_\n" if @_;
    die <<EOF;
usage: $0 [-a addr] [-c] [-f] [-n] [-m] [-s] switch [switch...]
    -f	do not use the cache (slow)
    -g  do not use global cache
    -c	force the cache to not expire
    -d	show port detail
    -n	print nodes as IP addresses, not hostnames
    -m	print nodes as MAC addresses
    -s	do not cache port detail (autoneg, port state, etc.)
    -a	find an addr

EOF
}

sub lookup;

getopts ("a:cdfgnms", \%opt) || usage ("argument error");

if (@ARGV) {
    @SWITCHLIST = @ARGV;
} else {
    @SWITCHLIST = `grep -v '^#' /home/mis/net/switch-config/switch-list`;
    chomp @SWITCHLIST;
}

die "no switches given\n" if (@SWITCHLIST == 0);

%HOSTHASH = ();

if ($opt{"a"}) {
    if ($opt{"a"} =~ /:/) {
    	$TO_FIND = $opt{"a"};
    } elsif ($opt{"a"} =~ /^\d+\.\d+\.\d+\.\d+$/) {
    	$TO_FIND = $opt{"a"};
    } elsif ($opt{"a"} =~ /^[a-z][a-z0-9-]*$/i) {
    	die "could not resolve host $opt{a}\n"
	    if (!defined ($TO_FIND = gethostbyname ($opt{"a"})));
	$TO_FIND = inet_ntoa ($TO_FIND);
    } else {
    	die "unidentified address type\n";
    }
}

#
# get ARP stuff
#
my $arp = new A3Com::ARP (
    SWITCH => "helium",
    GLOBALCACHE => 0,
    CACHE => 0,
);

die "couldn't get arp cache\n" if (!defined $arp);

$arphist = $arp->load_history ();

die "couldn't load history: " . $arp->error . "\n"
    if ($arp->error ne "");

if ($opt{"a"} && $TO_FIND =~ /\d+\./) {
    if (defined $arphist->{$TO_FIND}) {
    	$TO_FIND = $arphist->{$TO_FIND}->{"current"};
    } else {
    	die "IP address not found in ARP tables\n";
    }
}

if ($opt{"d"}) {
    print "#\n# switch port portState actualPortMode autoNegMode addrs\n#\n";
} else {
    print "#\n# switch port addrs\n#\n";
}

#
# walk through switches and display bridge tables (with IP addresses)
#
foreach $switch (@SWITCHLIST) {
    $sw = new A3Com::BridgeTable (
    	SWITCH => $switch,
	GLOBALCACHE => 1,
    );
    $sw->cache (0) if ($opt{"f"});
    $sw->globalcache (0) if ($opt{"g"});
    $sw->cachetime (time) if ($opt{"c"});

    if ($opt{"d"}) {
	$swdetail = new A3Com::EtherDetail (
	    SWITCH => $switch,
	);
	$sw->globalcache (0) if ($opt{"g"});
	$swdetail->cache (0) if ($opt{"s"});
	$swdetail->cachetime (time) if ($opt{"c"});
    }

    @ports = $sw->ports;
    die "error for $switch: " . $sw->error . "\n" if (@ports == 0);

    foreach $port (sort { $a <=> $b} @ports) {
	my $detail;
	if ($opt{"d"}) {
	    $detail = $swdetail->port_val ($port, "portState") .
		    " " . $swdetail->port_val ($port, "actualPortMode") .
		    " " . $swdetail->port_val ($port, "autoNegMode");
	}

	@macs = $sw->port ($port);
	if (@macs == ()) {
	    if ($opt{"d"}) {
		print "$switch $port $detail\n";
	    } else {
		print "$switch $port\n";
	    }
	    next;
	}

	if ($opt{"m"} && ! $opt{"a"}) {
	    if ($opt{"d"}) {
		print "$switch $port $detail @macs\n";
	    } else {
		print "$switch $port @macs\n";
	    }
	    next;
	}

	@addrs = ();
	foreach $mac (@macs) {
	    next if ($opt{"a"} && $mac ne $TO_FIND);

	    if ($opt{"m"}) {
	    	push @addrs, $mac;
		next;
	    }

	    # look at all of $arphist for IPs that resolve to $mac
	    @ips = $arp->lookup_mac_history ($arphist, $mac);

	    push @addrs, @ips;
	}

	next if ($opt{"a"} && @addrs == ());

	if (!$opt{"n"}) {
	    for (@addrs) {
		$_ = lookup ($_) unless (/:/);
	    }
	}

	if ($opt{"d"}) {
	    print "$switch $port $detail @addrs\n";
	} else {
	    print "$switch $port @addrs\n";
	}

    }
}


sub lookup {
    my $ip = shift;

    return $HOSTHASH{$ip} if (defined $HOSTHASH{$ip});
    my $IP = gethostbyaddr (inet_aton ($ip), AF_INET);
    if (!defined $IP) {
    	$HOSTHASH{$ip} = $ip;
	return $ip;
    }
    $IP =~ s/^([^.]+)\..*$/$1/;
    $HOSTHASH{$ip} = $IP;
}
