Article Figure 1 Figure 2 Listing 1 Listing 2 may2006.tar

Listing 1 db2dot

#!/usr/bin/perl

package main;

use strict;
use warnings;
$|++;

use GraphViz;
use NetAddr::IP;
use Getopt::Std;
use lib 'scripts';
use MyConfigCDBI;
require nodes;

use vars qw/%device_node %interface_node %subnet_node %if_edge
%subnet_node %sighting_edge %endpoint_node %addr_edge %graph_options
%addr_edge %assignment_edge $opt_3 $opt_e $opt_E $opt_s $opt_S $opt_d
$opt_D $opt_h $opt_i $opt_I $opt_N $opt_n $opt_v $opt_V/;

getopts('3d:D:e:E:hi:I:s:S:Nnv:V:');

die <<DIE if $opt_h;
db2dot [opts]

Where opts is up to one of

  -h: This help screen
  -d regexp: Perl regexp that the device must match to be added
  -D regexp: If the device matches this endpoint, is not added
  -e regexp: Perl regexp that the endpoint name must match to be added
  -E regexp: If the endpoint matches this regexp, is not added
  -i regexp: Perl regexp that the interface name must match to be added
  -I regexp: If the interface matches this regexp, is not added
  -s regexp: Perl regexp that the os of the endpoint must match to be added
  -S regexp: If the os matches this regexp, is not added
  -v regexp: Perl regexp that the vendor of the endpoint must match to be added
  -V regexp: If the vendor matches this regexp, is not added
  -3: Only include interfaces with IP addresses
  -n: Do not include subnets in the diagrams
  -N: Include only subnets with endpoints
  

DIE
    ;

my $g = GraphViz->new(%graph_options);
MyConfig::CDBI->connection('dbi:SQLite:dbname=config.db');

my %dev = ();
my %int = ();
my %eps = ();
my %subnets = ();
my %sightings = ();
my %assignments = ();

sub _ifname ($) { $_[0]->device . '|' . $_[0]->interface }

sub _store_dev
{
    my $dev = shift;
    return if exists $dev{$dev->device};
    $dev{$dev->device} = $dev;
}

sub _store_ep
{
    my $ep = shift;
    return if exists $eps{$ep};
    $eps{$ep} = $ep;
}

sub _store_int
{
    my $int = shift;
    my $l3 = shift;
    return if exists $int{_ifname $int};
    my @addrs = MyConfig::CDBI::Address->search
    (
     interface => $int->interface,
     device => $int->device,
    );
    return if $l3 and !@addrs;
    $int{_ifname $int} = { if => $int, addr => \@addrs };
}

if ($opt_d)
{
    for my $dev (MyConfig::CDBI::Device->retrieve_all)
    {
    next unless $dev->device =~ m/$opt_d/i;
    if ($opt_D) { next if $dev->device =~ m/$opt_D/i }
    _store_dev($dev);

    for my $ifs ($dev->interfaces)
    {
        if ($opt_i) { next unless $ifs->interface =~ m/$opt_i/i }
        if ($opt_I) { next if $ifs->interface =~ m/$opt_I/i }
        _store_int($ifs, $opt_3);
    }
    }
}

if ($opt_e or $opt_s or $opt_v)
{
    for my $ep (MyConfig::CDBI::Endpoint->retrieve_all)
    {
    if ($opt_e) { next unless $ep->endpoint =~ m/$opt_e/i }
    if ($opt_s) { next unless $ep->os and $ep->os =~ m/$opt_s/i }
    if ($opt_v) { next unless $ep->vendor and $ep->vendor =~ m/$opt_v/i }
    if ($opt_E) { next if $ep->endpoint =~ m/$opt_E/i }
    if ($opt_S) { next if $ep->os and $ep->os =~ m/$opt_S/i }
    if ($opt_V) { next if $ep->vendor and $ep->vendor =~ m/$opt_V/i }

    _store_ep($ep);

    for my $sight ($ep->all_sightings({order_by => 'time DESC'}))
    {
        $sightings{$ep}->{_ifname $sight} ||= [ $sight, 0 ];
        $sightings{$ep}->{_ifname $sight}->[1]++;
        _store_dev($sight->device);
        _store_int(MyConfig::CDBI::Interface->retrieve(
               interface => $sight->interface,
               device => $sight->device,
               ), 0);
    }

    for my $assign ($ep->all_assignments({order_by => 'time DESC'}))
    {
        $assignments{$ep}->{$assign->ip} ||= [ $assign, 0 ];
        $assignments{$ep}->{$assign->ip}->[1]++;
    }
    }
}

unless ($opt_n)
{
    unless ($opt_N)
    {
    for my $if (keys %int)
    {
        for my $a (@{$int{$if}->{addr}})
        {
        my $sn = $a->cidr;
        if ($sn) { $subnets{$sn->cidr} = $sn }
        else { 
            warn "Missing or unknown subnet from interface $if...\n";
        }
        }
    }
    }
    
    for my $ep (keys %assignments)
    {
    for my $ip (keys %{$assignments{$ep}})
    {
        my $sn = $assignments{$ep}->{$ip}->[0]->cidr;
        if ($sn) { $subnets{$sn->cidr} = $sn }
        else { warn "Missing or unknown subnet from assignment ", 
           $assignments{$ep}->{$ip}->[0], "...\n" }
    }
    }

    $g->add_node($_, label => $_, %subnet_node)
    for keys %subnets;
}

$g->add_node($_, label => $dev{$_}->device, dev_image($dev{$_}->class),
         %device_node) 
    for keys %dev;

for my $if (keys %int)
{
    $g->add_node($if, label => $int{$if}->{if}->interface, %interface_node);
    $g->add_edge($int{$if}->{if}->device => $if,
         ($int{$if}->{if}->description ? 
          (label => $int{$if}->{if}->description) : ()),
         %if_edge);
    unless ($opt_n)
    {
    for my $a (@{$int{$if}->{addr}})
    {
        next if $opt_N and not exists $subnets{$a->get('cidr')};
        $g->add_edge($if => $a->get('cidr') || '?',
             label => NetAddr::IP->new($a->ip)->addr,
             %addr_edge);
    }
    }
}

for my $ep (keys %eps)
{
    $g->add_node($ep, label => $eps{$ep}->endpoint . "\n" . $eps{$ep}->vendor, 
         %endpoint_node);
    if (exists $sightings{$ep})
    {
    my $sight = $sightings{$ep};
    for my $if (keys %$sight)
    {
        $g->add_edge(
        $ep => $if,
        label => $sight->{$if}->[1] . "x " 
        . scalar(localtime($sight->{$if}->[0]->time)),
        %sighting_edge
        );
    }
    }

    if (!$opt_n and exists $assignments{$ep})
    {
    my $assign = $assignments{$ep};
    for my $a (keys %$assign)
    {
        $g->add_edge(
        $ep => $assign->{$a}->[0]->get('cidr') || '?',
        label => $assign->{$a}->[1] . "x " 
        . NetAddr::IP->new($assign->{$a}->[0]->ip)->addr,
        %assignment_edge
        );
    }
    }
}

print $g->as_debug;