#!/usr/local/bin/perl # $Id: track-dot1d,v 1.2 1999/07/03 16:55:26 maf Exp $ require "getopts.pl"; use SNMP 1.8; use Time::ParseDate; # # SNMP does not yet implement a sess->close() or sess->update so the main # loop is probably leaking memory. # &Getopts('s:r:hl:L:f:F:M:H:I:'); if ($opt_h) { print STDERR "Usage: track-dot1d -s dir -r n -l date options: -s set the data directory -r1 MAC addresses / host ifIndex report -r2 Last MAC address / host ifIndex report -r3 host ifIndex / MAC address report -r4 summary totals -r5 totals / host ifIndex report -r6 1 without the fluff limit reports to date with -F first seen >= date -f first seen < date -L last seen >= date -l last seen < date limit reports with -M macaddr -H host -I interface Feed on stdin. Ignore-list is a list of interfaces to ignore during reports. When not in reporting mode, will gather the bridge learn table via SNMP. "; exit 0; } $DATADIR= ($opt_s eq '') ? "/var/stats/dot1d/" : "$opt_s/"; if ($opt_l ne '') { if ($opt_l ne 'last') { $opt_l = parsedate($opt_l); } } if ($opt_L ne '') { if ($opt_L ne 'last') { $opt_L = parsedate($opt_L); } } if ($opt_f ne '') { if ($opt_f ne 'last') { $opt_f = parsedate($opt_f); } } if ($opt_F ne '') { if ($opt_F ne 'last') { $opt_F = parsedate($opt_F); } } while (<>) { chomp; ($host, $com, @ignore) = split; $err = 0; my $rh = {}; my $ra = []; &load($host, $ra, $rh); # &dump($host, $ra, $rh); if (($opt_r eq '')) { $now = time; &get($host, $com, $rh); &save("$host.new", $ra, $rh, $now); rename "$DATADIR/$host", "$DATADIR/$host.old"; rename "$DATADIR/$host.new", "$DATADIR/$host"; undef $host; undef @ltimes; undef $rh; } else { $t = $ra->[@$ra-1]; $opt_f = ($opt_f eq 'last') ? $t : $opt_f; $opt_F = ($opt_F eq 'last') ? $t : $opt_F; $opt_l = ($opt_l eq 'last') ? $t : $opt_l; $opt_L = ($opt_L eq 'last') ? $t : $opt_L; $dblist{$host} = $rh; # $dblist{"$host.times"} = $ra; $ignore{"$host"} = \@ignore; } } if ($opt_r) { $sub = "report$opt_r"; &$sub(\%dblist); } sub get { my ($host, $com, $ent) = @_; my ($z, @d, $ra, $mac, $first, $last, $found); my $vars = new SNMP::VarList(['dot1dTpFdbPort'],['dot1dTpFdbStatus']); my $sess = new SNMP::Session ( DestHost => $host, Community => $com); if (!defined $sess) { print STDERR "skipping $host\n"; return 0; } &fix; for (@vals = $sess->getnext($vars); $vars->[0]->tag =~ /dot1dTpFdbPort/ # still in table and not $sess->{ErrorStr}; # and not end of mib or other error @vals = $sess->getnext($vars)) { # normalize mac addr undef @d; foreach (split(/\./, $vars->[0]->iid)) { push @d, sprintf("%2.2X", $_); } $z = join ':', @d; # skip 'self' next if ($vals[1] == 4); $found = 0; if (defined ($ent->{$vals[0]})) { $ra = $ent->{$vals[0]}; } else { $ra = $ent->{$vals[0]} = []; } foreach (@$ra) { ($mac, $first, $last) = split; if ($mac eq $z) { $last = time; $_ = "$mac $first $last"; $found = 1; last; } } if (!$found) { $first = $last = time; push @$ra, "$z $first $last"; } } } # get sub dump { my ($host, $ltimes, $ent) = @_; my $ra; print "Host: $host ", join "-", @$ltimes, "\n"; foreach (sort numerically (keys(%$ent))) { print "Interface: $_ ", scalar @{$ent->{$_}}, "\n"; foreach $x (@{$ent->{$_}}) { print "Entry: $x\n"; } } } # dump sub save { my ($host, $ltimes, $ent, $now) = @_; my $x; if (!open (DB, ">$DATADIR/$host")) { print STDERR "open(>$DATADIR/$host): $!\n"; return -1; } foreach (@$ltimes) { print DB "T $_\n"; } print DB "T $now\n"; foreach (keys(%$ent)) { print DB "I $_ ", scalar @{$ent->{$_}}, "\n"; foreach $x (@{$ent->{$_}}) { print DB "E $x\n"; } } } # save sub load { my ($host, $ltimes, $ent) = @_; my ($ra, $ifIndex, $ifEntries, $mac, $first, $last); local *DB; if (open (DB, "<$DATADIR/$host")) { while () { chomp; # run times if (/^T /) { $_ =~ s/^T //; push @$ltimes, $_; next; } # ifIndex if (/^I /) { $_ =~ s/^I //; ($ifIndex, $ifEntries) = split; $ra = []; $ent->{$ifIndex} = $ra; next; } # Entry if (/^E /) { $_ =~ s/^E //; ($mac, $first, $last) = split; push @$ra, "$mac $first $last"; next; } print STDERR "Unknown: $_\n"; } } } # load sub fix { if (!$need_fix) { &SNMP::addMibFiles("/usr/local/ucdsnmp353/share/snmp/mibs/RFC1213-MIB.txt","/usr/local/ucdsnmp353/share/snmp/mibs/BRIDGE-MIB.txt"); $need_fix = 1; } } # # report 1 # sub report1 { my ($rh) = @_; my ($rh2, $ra, $skip, $h, $i); $~ = "REPORT1"; foreach $h (keys(%$rh)) { next if (($opt_H ne '') && ($opt_H ne $h)); $rh2 = $rh->{$h}; foreach $i (sort numerically (keys(%$rh2))) { next if (($opt_I ne '') && ($opt_I ne $i)); $ra = $rh2->{$i}; foreach (@{$ignore{$h}}) { if ($_ eq $i) { undef $ra; last; } } ($host) = split(/\./,$h); $ifIndex = $i; foreach (sort esort (@$ra)) { ($mac, $first, $last) = split; next if (($opt_M ne '') && ($opt_M ne $mac)); next if (!check_date($first, $last)); $first = ftime($first); $last = ftime($last); write; $host = $ifIndex = '^' } } } } # report1 # # report 2 # sub report2 { my ($rh) = @_; my ($rh2, $ra, $skip, @sorted, $h); $~ = "REPORT2"; foreach $h (keys(%$rh)) { next if (($opt_H ne '') && ($opt_H ne $h)); $rh2 = $rh->{$h}; foreach $ifIndex (sort numerically (keys(%$rh2))) { next if (($opt_I ne '') && ($opt_I ne $ifIndex)); $ra = $rh2->{$ifIndex}; foreach (@{$ignore{$h}}) { if ($_ eq $ifIndex) { undef $ra; last; } } ($host) = split(/\./,$h); if (@$ra) { @sorted = sort esort (@$ra) ; $_ = $sorted[$#sorted]; ($mac, $first, $last) = split; next if (($opt_M ne '') && ($opt_M ne $mac)); next if (!check_date($first, $last)); $last = ftime($last); write; } } } } # report2 # # report 3 # sub report3 { my ($rh) = @_; my ($rh2, $ra, $skip, $h, $i, $m); $~ = "REPORT3"; foreach $h (keys(%$rh)) { next if (($opt_H ne '') && ($opt_H ne $h)); $rh2 = $rh->{$h}; foreach $i (sort numerically (keys(%$rh2))) { next if (($opt_I ne '') && ($opt_I ne $i)); $ra = $rh2->{$i}; foreach (@{$ignore{$h}}) { if ($_ eq $i) { undef $ra; last; } } foreach (@$ra) { ($mac, $first, $last) = split; next if (!check_date($first, $last)); next if (($opt_M ne '') && ($opt_M ne $mac)); if (!defined $m{$mac}) { $m{$mac} = []; } push @{$m{$mac}}, "$h $i $first $last"; } } } foreach (sort (keys(%m))) { $mac = $_; $ra = $m{$_}; foreach (@$ra) { ($h, $ifIndex, $first, $last) = split; ($host) = split(/\./,$h); write; $mac = '^'; } } } # report3 # # report 4 # sub report4 { my ($rh) = @_; my ($rh2, $ra, $skip, %mcount); $~ = "REPORT4"; $total_hosts = 0; $total_counted_interfaces = 0; $total_skipped_interfaces = 0; $total_macs = 0; foreach $host (keys(%$rh)) { next if (($opt_H ne '') && ($opt_H ne $h)); ++$total_hosts; $rh2 = $rh->{$host}; foreach $ifIndex (sort numerically (keys(%$rh2))) { next if (($opt_I ne '') && ($opt_I ne $ifIndex)); $ra = $rh2->{$ifIndex}; foreach (@{$ignore{$host}}) { if ($_ eq $ifIndex) { undef $ra; ++$total_skipped_interfaces; last; } } ++$total_counted_interfaces; foreach (@$ra) { ($mac, $first, $last) = split; next if (($opt_M ne '') && ($opt_M ne $mac)); next if (!check_date($first, $last)); $mcount{$mac} = 1; } } } $total_macs = scalar keys(%mcount); write; } # report4 # # report 5 # sub report5 { my ($rh) = @_; my ($rh2, $ra, $h); $~ = "REPORT5"; foreach $h (keys(%$rh)) { next if (($opt_H ne '') && ($opt_H ne $h)); $rh2 = $rh->{$h}; foreach $ifIndex (sort numerically (keys(%$rh2))) { next if (($opt_I ne '') && ($opt_I ne $ifIndex)); $ra = $rh2->{$ifIndex}; foreach (@{$ignore{$h}}) { if ($_ eq $ifIndex) { undef $ra; last; } } $count = 0; foreach (@$ra) { ($mac, $first, $last) = split; next if (($opt_M ne '') && ($opt_M ne $mac)); next if (!check_date($first, $last)); ++$count; } ($host) = split(/\./,$h); write if ($count); } } } # report5 # # report 6 # sub report6 { my ($rh) = @_; my ($rh2, $ra, $skip, $h, $i); $~ = "REPORT1"; foreach $h (keys(%$rh)) { next if (($opt_H ne '') && ($opt_H ne $h)); $rh2 = $rh->{$h}; foreach $i (sort numerically (keys(%$rh2))) { next if (($opt_I ne '') && ($opt_I ne $i)); $ra = $rh2->{$i}; foreach (@{$ignore{$h}}) { if ($_ eq $i) { undef $ra; last; } } ($host) = split(/\./,$h); $ifIndex = $i; foreach (sort esort (@$ra)) { ($mac, $first, $last) = split; next if (($opt_M ne '') && ($opt_M ne $mac)); next if (!check_date($first, $last)); $first = ftime($first); $last = ftime($last); print "$host $ifIndex $mac $first $last\n"; } } } } # report6 sub numerically { $a <=> $b; } sub ftime { my ($d) = @_; my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isday); ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdsy) = localtime($d); $year += 1900; $mday += 1; $mon += 1; "$mon/$mday/$year $hour:$min:$sec"; } sub esort { my ($mac1, $first1, $last1); my ($mac2, $first2, $last2); ($mac1, $first1, $last1) = split(/ /,$1); ($mac2, $first2, $last2) = split(/ /,$2); $first1 <=> $first2; } sub check_date { my ($first, $last) = @_; my ($pass); $pass = 1; while (1) { if ($opt_l && ($last > $opt_l)) { $pass = 0; last; } if ($opt_L && ($last <= $opt_L)) { $pass = 0; last; } if ($opt_f && ($first > $opt_f)) { $pass = 0; last; } if ($opt_F && ($first <= $opt_F)) { $pass = 0; last } last; } $pass; } format REPORT1_TOP = All MAC addresses / interface Host Interface Address First Last ------------------------------------------------------------------------------- . format REPORT2_TOP = Last MAC addresses / interface Host Interface Address Last ------------------------------------------------------------------------------- . format REPORT3_TOP = MAC addresses / interface Host Interface Address Last ------------------------------------------------------------------------------- . format REPORT5_TOP = MAC counts / Interface Host Interface Count ------------------------------------------------------------------------------- . format REPORT1 = @<<<<<<<<<<<<@<<<<<<@<<<<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<<<< $host,$ifIndex,$mac,$first,$last . format REPORT2 = @<<<<<<<<<<<<@<<<<<<@<<<<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<<<<<< $host,$ifIndex,$mac,$last . format REPORT3 = @<<<<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<<<<<<<<<<<<@<<<<<<<< $mac,$host,$ifIndex . format REPORT4 = Hosts @<<<<<<<<<<<<<< $total_hosts Counted Interfaces @<<<<<<<<<<<<<< $total_counted_interfaces Skipped Interfaces @<<<<<<<<<<<<<< $total_skipped_interfaces MAC Addresses @<<<<<<<<<<<<<< $total_macs . format REPORT5 = @<<<<<<<<<<<<@<<<<<<@<<<<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<<<< $host,$ifIndex,$count .