]> mj.ucw.cz Git - jablonka.git/blobdiff - show-switch
show-switch: work-around for broken hispeed
[jablonka.git] / show-switch
index 60e166f13429918bf3ed599a7970cf82dd431cbe..9dab19c7317dc074e0c2db19c9adf85d630a7b80 100755 (executable)
@@ -6,21 +6,37 @@ use Net::SNMP ();
 use Net::Netmask;
 use Data::Dumper;
 use Getopt::Long;
+use List::Util;
+
+no warnings 'uninitialized';
 
 sub usage {
-       die "Usage: $0 [--debug] [--mac] <ip-addr>\n";
+       die <<AMEN ;
+Usage: $0 [<options>] <switch>
+
+Options:
+--debug                Show debugging outputs
+--mac          Show one MAC address learned per port
+--allmac       Show all MAC addresses learned per port
+--force-v1     Use SNMPv1 (work-around for buggy switches)
+AMEN
 }
 
 my $debug = 0;
 my $mac = 0;
+my $all_mac = 0;
+my $force_v1 = 0;
 GetOptions(
        'debug' => \$debug,
        'mac' => \$mac,
+       'allmac' => \$all_mac,
+       'force-v1' => \$force_v1,
 ) or usage;
 
 @ARGV == 1 or usage;
 my ($switch_ip) = @ARGV;
 my $community = 'public';
+$mac ||= $all_mac;
 
 my $is_tty = -t STDOUT;
 sub attr {
@@ -30,11 +46,12 @@ sub attr {
 my $t_red = attr("setaf 1");
 my $t_green = attr("setaf 2");
 my $t_yellow = attr("setaf 3");
+my $t_magenta = attr("setaf 5");
 my $t_norm = attr("sgr0");
 
 my ($snmp, $err) = Net::SNMP->session(
        -hostname => $switch_ip,
-       -version => '2c',
+       -version => ($force_v1 ? '1' : '2c'),
        -community => $community,
 );
 $snmp or die "Cannot establish session: $err\n";
@@ -75,8 +92,12 @@ my $basics = my_get_table({
        'name' => "$OID_basic.5",
        'location' => "$OID_basic.6",
 });
-print Dumper($basics) if $debug;
+print "# Basics:\n", Dumper($basics) if $debug;
 my $bas = $basics->{0} or die "Cannot find basic info";
+for (values %$bas) {
+       s{\r}{}gs;
+       s{\n}{ | }gs;
+}
 
 print "### Basics ###\n\n";
 print "Device:   ", $bas->{desc}, "\n";
@@ -90,22 +111,33 @@ my $OID_ifTablev2 = '1.3.6.1.2.1.31.1.1';
 
 my $if_table = my_get_table({
        'desc' => "$OID_ifTable.1.2",
-       'name' => "$OID_ifTablev2.1.1",
-       'alias' => "$OID_ifTablev2.1.18",
        'speed' => "$OID_ifTable.1.5",
-       'hispeed' => "$OID_ifTablev2.1.15",
+       'mac' => "$OID_ifTable.1.6",
        'admin' => "$OID_ifTable.1.7",
        'oper' => "$OID_ifTable.1.8",
+       'name' => "$OID_ifTablev2.1.1",
+       'hispeed' => "$OID_ifTablev2.1.15",
+       'alias' => "$OID_ifTablev2.1.18",
 });
-print Dumper($if_table) if $debug;
+my %if_macs = ();
+for my $if (values %$if_table) {
+       $if->{mac} = join(':', map { sprintf "%02x", ord $_ } split(//, $if->{mac}));
+       $if_macs{$if->{mac}} = 1;
+       if ($if->{hispeed} == $if->{speed}) {
+               # Some buggy switches put hispeed == speed
+               delete $if->{hispeed};
+       }
+}
+print "# Interface table:\n", Dumper($if_table) if $debug;
 my @ifaces = sort { $a <=> $b } keys %$if_table;
+print "MAC addr: ", join(" ", sort keys %if_macs), "\n";
 
 my $OID_ipTable = '1.3.6.1.2.1.4.20';
 my $ip_table = my_get_table({
        'iface' => "$OID_ipTable.1.2",
        'mask' => "$OID_ipTable.1.3",
 });
-print Dumper($ip_table) if $debug;
+print "# IP table:\n", Dumper($ip_table) if $debug;
 
 # XXX: IPv6 not supported yet
 for my $ipa (keys %$ip_table) {
@@ -114,25 +146,62 @@ for my $ipa (keys %$ip_table) {
        my $nm = Net::Netmask->new2($ipa, $ip->{mask}) or die "Cannot parse IP prefix";
        push @{$if->{ip_addrs}}, $ipa . '/' . $nm->bits;
 }
-print Dumper($if_table) if $debug;
+
+# Switch ports
+my $OID_1dBasePortTable = '1.3.6.1.2.1.17.1.4';
+my $port_table = my_get_table({
+       'ifindex' => "$OID_1dBasePortTable.1.2",
+});
+print "# Switch ports:\n", Dumper($port_table) if $debug;
+
+for my $port (keys %$port_table) {
+       my $if = $if_table->{$port_table->{$port}->{ifindex}} or die "Port table refers to unknown iface";
+       $if->{switch_port} = $port;
+}
+
+print "# Extended interface table:\n", Dumper($if_table) if $debug;
 
 if ($mac) {
        my $OID_1qTpFdbTable = '1.3.6.1.2.1.17.7.1.2.2';
-       my $OID_1qPort = "$OID_1qTpFdbTable.1.2";
-       my $OID_1qStatus = "$OID_1qTpFdbTable.1.3";
        my $vlan_fdb_table = my_get_table({
-               'port' => $OID_1qPort,
-               'status' => $OID_1qStatus,
+               'port' => "$OID_1qTpFdbTable.1.2",
+               'status' => "$OID_1qTpFdbTable.1.3",
        });
-       print Dumper($vlan_fdb_table) if $debug;
-
-       for my $m (keys %$vlan_fdb_table) {
-               my $fdb = $vlan_fdb_table->{$m};
-               my $port = $if_table->{$fdb->{port}} or die "Forwarding DB refers to unknown iface";
-               my @m = split /\./, $m;
-               my $vlan = shift @m;
-               my $mac = join(':', map { sprintf('%02x', $_) } @m);
-               push @{$port->{macs}}, $mac;
+       print "# VLAN FDB:\n", Dumper($vlan_fdb_table) if $debug;
+
+       if (%$vlan_fdb_table) {
+               for my $m (keys %$vlan_fdb_table) {
+                       my $fdb = $vlan_fdb_table->{$m};
+                       $fdb->{status} == 3 or next;    # Only learned MACs
+                       my $port = $port_table->{$fdb->{port}} or die "Forwarding DB refers to unknown port";
+                       my $if = $if_table->{$port->{ifindex}} or die "Forwarding DB refers to unknown iface";
+                       my @m = split /\./, $m;
+                       my $vlan = shift @m;
+                       my $mac = join(':', map { sprintf('%02x', $_) } @m);
+                       push @{$if->{macs}}, $mac;
+               }
+       } else {
+               print "# Trying fall-fack to .1d FDB\n" if $debug;
+               my $OID_1dTpFdbTable = '1.3.6.1.2.1.17.4.3';
+               my $fdb_table = my_get_table({
+                       'port' => "$OID_1dTpFdbTable.1.2",
+                       'status' => "$OID_1dTpFdbTable.1.3",
+               });
+               print "# Non-VLAN FDB:\n", Dumper($fdb_table) if $debug;
+               for my $m (keys %$fdb_table) {
+                       my $fdb = $fdb_table->{$m};
+                       $fdb->{status} == 3 or next;    # Only learned MACs
+                       my $port = $port_table->{$fdb->{port}} or die "Forwarding DB refers to unknown port";
+                       my $if = $if_table->{$port->{ifindex}} or die "Forwarding DB refers to unknown iface";
+                       my @m = split /\./, $m;
+                       my $mac = join(':', map { sprintf('%02x', $_) } @m);
+                       push @{$if->{macs}}, $mac;
+               }
+       }
+
+       for my $if (values %$if_table) {
+               $if->{macs} or next;
+               $if->{macs} = [ sort(List::Util::uniq(@{$if->{macs}})) ];
        }
 }
 
@@ -148,7 +217,7 @@ for my $vlan (values %$vlan_table) {
                $vlan->{$k} = [ split //, unpack("B*", $vlan->{$k} // "") ];
        }
 }
-print Dumper($vlan_table) if $debug;
+print "# VLAN table\n", Dumper($vlan_table) if $debug;
 my @vlans = sort { $a <=> $b } grep { $vlan_table->{$_}->{'row-status'} == 1 } keys %$vlan_table;
 
 print "\n### VLANs ###\n\n";
@@ -161,9 +230,9 @@ if (@vlans) {
 }
 
 print "\n### Ports ###\n\n";
-# XXX: We assume that 802.1d switch ports IDs are equal to interface IDs
-for my $port (@ifaces) {
-       my $if = $if_table->{$port};
+for my $ifindex (@ifaces) {
+       my $if = $if_table->{$ifindex};
+       my $port = $if->{switch_port};
        my $state;
        my $scolor = $t_norm;
        if ($if->{'admin'} != 1) {
@@ -184,17 +253,17 @@ for my $port (@ifaces) {
                $speed = int($speed/1000);
                $speed = "${speed}G";
        }
-       printf "%-4d %-15.15s  %s%-4s %-5s %s%-20s%s", $port, $if->{name}, $scolor, $state, $speed, $t_yellow, $if->{alias}, $t_norm;
+       printf "%-4d %-15.15s  %s%-4s %-5s %s%-25.25s%s", $ifindex, $if->{name}, $scolor, $state, $speed, $t_yellow, $if->{alias}, $t_norm;
 
        if ($mac) {
-               my $macs = $if->{macs};
                my $show_mac = "";
                my $more_macs = " ";
-               if ($macs && @$macs) {
-                       $show_mac = $macs->[0];
-                       $more_macs = "${t_yellow}+${t_norm}" if @$macs > 1;
+               my @macs = @{$if->{macs} // []};
+               if (@macs) {
+                       $show_mac = $macs[0];
+                       $more_macs = "${t_yellow}+${t_norm}" if @macs > 1;
                }
-               printf "%-17s%s ", $show_mac, $more_macs;
+               printf " %-17s%s ", $show_mac, $more_macs;
        }
 
        for my $vid (@vlans) {
@@ -217,4 +286,14 @@ for my $port (@ifaces) {
        if ($if->{ip_addrs}) {
                print "${t_yellow}     IP: ", join(" ", @{$if->{ip_addrs}}), "${t_norm}\n";
        }
+       if ($all_mac) {
+               if ($if->{mac}) {
+                       print "${t_yellow}     Local MAC: ", $if->{mac}, "${t_norm}\n";
+               }
+               if ($if->{macs}) {
+                       for my $m (@{$if->{macs}}) {
+                               print "${t_magenta}     MAC: $m${t_norm}\n";
+                       }
+               }
+       }
 }