]> mj.ucw.cz Git - jablonka.git/blob - show-switch
10337597e67780abcee4e8f78e0bb575014de291
[jablonka.git] / show-switch
1 #!/usr/bin/perl
2
3 use strict;
4 use warnings;
5 use Net::SNMP ();
6 use Net::Netmask;
7 use Data::Dumper;
8 use Getopt::Long;
9 use List::Util;
10
11 sub usage {
12         die "Usage: $0 [--debug] [--mac] [--allmac] <ip-addr>\n";
13 }
14
15 my $debug = 0;
16 my $mac = 0;
17 my $all_mac = 0;
18 GetOptions(
19         'debug' => \$debug,
20         'mac' => \$mac,
21         'allmac' => \$all_mac,
22 ) or usage;
23
24 @ARGV == 1 or usage;
25 my ($switch_ip) = @ARGV;
26 my $community = 'public';
27 $mac ||= $all_mac;
28
29 my $is_tty = -t STDOUT;
30 sub attr {
31         my ($a) = @_;
32         return $is_tty ? `tput $a` : "";
33 }
34 my $t_red = attr("setaf 1");
35 my $t_green = attr("setaf 2");
36 my $t_yellow = attr("setaf 3");
37 my $t_magenta = attr("setaf 5");
38 my $t_norm = attr("sgr0");
39
40 my ($snmp, $err) = Net::SNMP->session(
41         -hostname => $switch_ip,
42         -version => '2c',
43         -community => $community,
44 );
45 $snmp or die "Cannot establish session: $err\n";
46 $snmp->translate(0);
47
48 sub my_get_table {
49         my ($cols) = @_;
50         my $tab = {};
51         for my $c (keys %$cols) {
52                 my $depth = split /\./, $cols->{$c};
53                 my $t = $snmp->get_table(-baseoid => $cols->{$c}) or next;
54                 for my $k (keys %$t) {
55                         my @k = split /\./, $k;
56                         my $kk = join('.', @k[$depth..$#k]);
57                         $tab->{$kk}->{$c} = $t->{$k};
58                 }
59         }
60         return $tab;
61 }
62
63 sub format_uptime {
64         my ($t) = @_;
65         my $d = "";
66         $t = int($t/100);
67         if ($t >= 86400) {
68                 $d = int($t/86400) . " days, ";
69                 $t %= 86400;
70         }
71         return $d . sprintf "%02d:%02d:%02d", int($t/3600), int(($t%3600)/60), $t%60;
72 }
73
74 my $OID_basic = '1.3.6.1.2.1.1';
75
76 my $basics = my_get_table({
77         'desc' => "$OID_basic.1",
78         'uptime' => "$OID_basic.3",
79         'contact' => "$OID_basic.4",
80         'name' => "$OID_basic.5",
81         'location' => "$OID_basic.6",
82 });
83 print Dumper($basics) if $debug;
84 my $bas = $basics->{0} or die "Cannot find basic info";
85
86 print "### Basics ###\n\n";
87 print "Device:   ", $bas->{desc}, "\n";
88 print "Uptime:   ", format_uptime($bas->{uptime}), "\n";
89 print "Contact:  ", $bas->{contact}, "\n";
90 print "Name:     ", $bas->{name}, "\n";
91 print "Location: ", $bas->{location}, "\n";
92
93 my $OID_ifTable = '1.3.6.1.2.1.2.2';
94 my $OID_ifTablev2 = '1.3.6.1.2.1.31.1.1';
95
96 my $if_table = my_get_table({
97         'desc' => "$OID_ifTable.1.2",
98         'speed' => "$OID_ifTable.1.5",
99         'mac' => "$OID_ifTable.1.6",
100         'admin' => "$OID_ifTable.1.7",
101         'oper' => "$OID_ifTable.1.8",
102         'name' => "$OID_ifTablev2.1.1",
103         'hispeed' => "$OID_ifTablev2.1.15",
104         'alias' => "$OID_ifTablev2.1.18",
105 });
106 my %if_macs = ();
107 for my $if (values %$if_table) {
108         $if->{mac} = join(':', map { sprintf "%02x", ord $_ } split(//, $if->{mac}));
109         $if_macs{$if->{mac}} = 1;
110 }
111 print Dumper($if_table) if $debug;
112 my @ifaces = sort { $a <=> $b } keys %$if_table;
113 print "MAC addr: ", join(" ", sort keys %if_macs), "\n";
114
115 my $OID_ipTable = '1.3.6.1.2.1.4.20';
116 my $ip_table = my_get_table({
117         'iface' => "$OID_ipTable.1.2",
118         'mask' => "$OID_ipTable.1.3",
119 });
120 print Dumper($ip_table) if $debug;
121
122 # XXX: IPv6 not supported yet
123 for my $ipa (keys %$ip_table) {
124         my $ip = $ip_table->{$ipa};
125         my $if = $if_table->{$ip->{iface}} or die "IP table refers to unknown iface";
126         my $nm = Net::Netmask->new2($ipa, $ip->{mask}) or die "Cannot parse IP prefix";
127         push @{$if->{ip_addrs}}, $ipa . '/' . $nm->bits;
128 }
129 print Dumper($if_table) if $debug;
130
131 if ($mac) {
132         my $OID_1qTpFdbTable = '1.3.6.1.2.1.17.7.1.2.2';
133         my $OID_1qPort = "$OID_1qTpFdbTable.1.2";
134         my $OID_1qStatus = "$OID_1qTpFdbTable.1.3";
135         my $vlan_fdb_table = my_get_table({
136                 'port' => $OID_1qPort,
137                 'status' => $OID_1qStatus,
138         });
139         print Dumper($vlan_fdb_table) if $debug;
140
141         for my $m (keys %$vlan_fdb_table) {
142                 my $fdb = $vlan_fdb_table->{$m};
143                 $fdb->{status} == 3 or next;    # Only learned MACs
144                 my $port = $if_table->{$fdb->{port}} or die "Forwarding DB refers to unknown iface";
145                 my @m = split /\./, $m;
146                 my $vlan = shift @m;
147                 my $mac = join(':', map { sprintf('%02x', $_) } @m);
148                 push @{$port->{macs}}, $mac;
149         }
150         for my $if (values %$if_table) {
151                 $if->{macs} or next;
152                 $if->{macs} = [ sort(List::Util::uniq(@{$if->{macs}})) ];
153         }
154 }
155
156 my $OID_VlanStaticTable = '1.3.6.1.2.1.17.7.1.4.3';
157 my $vlan_table = my_get_table({
158         'name' => "$OID_VlanStaticTable.1.1",
159         'egress-ports' => "$OID_VlanStaticTable.1.2",
160         'untagged-ports' => "$OID_VlanStaticTable.1.4",
161         'row-status' => "$OID_VlanStaticTable.1.5",
162 });
163 for my $vlan (values %$vlan_table) {
164         for my $k ('egress-ports', 'untagged-ports') {
165                 $vlan->{$k} = [ split //, unpack("B*", $vlan->{$k} // "") ];
166         }
167 }
168 print Dumper($vlan_table) if $debug;
169 my @vlans = sort { $a <=> $b } grep { $vlan_table->{$_}->{'row-status'} == 1 } keys %$vlan_table;
170
171 print "\n### VLANs ###\n\n";
172 if (@vlans) {
173         for my $vid (@vlans) {
174                 printf "%-4d  %s\n", $vid, $vlan_table->{$vid}->{'name'} // '-';
175         }
176 } else {
177         print "No VLAN support.\n";
178 }
179
180 print "\n### Ports ###\n\n";
181 # XXX: We assume that 802.1d switch ports IDs are equal to interface IDs
182 for my $port (@ifaces) {
183         my $if = $if_table->{$port};
184         my $state;
185         my $scolor = $t_norm;
186         if ($if->{'admin'} != 1) {
187                 $state = 'OFF';
188         } elsif ($if->{'oper'} != 1) {
189                 $state = 'DOWN';
190                 $scolor = $t_red;
191         } else {
192                 $state = 'UP';
193                 $scolor = $t_green;
194         }
195         my $speed = $if->{hispeed} || int($if->{speed} / 1000000);
196         if (!$speed) {
197                 $speed = "";
198         } elsif ($speed < 1000) {
199                 $speed = "${speed}M";
200         } else {
201                 $speed = int($speed/1000);
202                 $speed = "${speed}G";
203         }
204         printf "%-4d %-15.15s  %s%-4s %-5s %s%-20s%s", $port, $if->{name}, $scolor, $state, $speed, $t_yellow, $if->{alias}, $t_norm;
205
206         if ($mac) {
207                 my $show_mac = "";
208                 my $more_macs = " ";
209                 my @macs = @{$if->{macs} // []};
210                 if (@macs) {
211                         $show_mac = $macs[0];
212                         $more_macs = "${t_yellow}+${t_norm}" if @macs > 1;
213                 }
214                 printf "%-17s%s ", $show_mac, $more_macs;
215         }
216
217         for my $vid (@vlans) {
218                 my $vlan = $vlan_table->{$vid};
219                 if ($vlan->{'egress-ports'}->[$port]) {
220                         if ($vlan->{'untagged-ports'}->[$port]) {
221                                 if ($t_green ne "") {
222                                         print " ${t_green}${vid}${t_norm}";
223                                 } else {
224                                         print " ${vid}U";
225                                 }
226                         } else {
227                                 print " $vid";
228                         }
229                 }
230         }
231
232         print "\n";
233
234         if ($if->{ip_addrs}) {
235                 print "${t_yellow}     IP: ", join(" ", @{$if->{ip_addrs}}), "${t_norm}\n";
236         }
237         if ($all_mac) {
238                 if ($if->{mac}) {
239                         print "${t_yellow}     Local MAC: ", $if->{mac}, "${t_norm}\n";
240                 }
241                 if ($if->{macs}) {
242                         for my $m (@{$if->{macs}}) {
243                                 print "${t_magenta}     MAC: $m${t_norm}\n";
244                         }
245                 }
246         }
247 }