File Coverage

blib/lib/OSPF/LSDB/View6.pm
Criterion Covered Total %
statement 826 894 92.3
branch 183 284 64.4
condition 77 115 66.9
subroutine 42 43 97.6
pod 1 39 2.5
total 1129 1375 82.1


line stmt bran cond sub pod time code
1             ##########################################################################
2             # Copyright (c) 2010-2021 Alexander Bluhm
3             #
4             # Permission to use, copy, modify, and distribute this software for any
5             # purpose with or without fee is hereby granted, provided that the above
6             # copyright notice and this permission notice appear in all copies.
7             #
8             # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9             # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10             # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11             # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12             # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13             # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14             # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15             ##########################################################################
16              
17 11     11   2209 use strict;
  11         24  
  11         363  
18 11     11   60 use warnings;
  11         21  
  11         538  
19              
20             =pod
21              
22             =head1 NAME
23              
24             OSPF::LSDB::View6 - display OSPF for IPv6 database as graphviz dot
25              
26             =head1 SYNOPSIS
27              
28             use OSPF::LSDB;
29              
30             use OSPF::LSDB::View6;
31              
32             my $ospf = OSPF::LSDB-Enew();
33              
34             my $view = OSPF::LSDB::View6-Enew($ospf);
35              
36             my $dot = view-Egraph();
37              
38             =head1 DESCRIPTION
39              
40             The OSPF::LSDB::View6 module converts the IPv6 content of a
41             L instance into a graphviz dot string.
42              
43             Most of OSPF::LSDB::View6 is derived from L.
44             Only differences between the v2 and v3 protocoll are implemented
45             and documented by this module.
46              
47             =cut
48              
49             package OSPF::LSDB::View6;
50 11     11   81 use base 'OSPF::LSDB::View';
  11         22  
  11         2824  
51 11         100 use fields qw (
52             sumlsids
53             boundlsids
54             externlsids
55             lnkhash
56             intraroutehash
57             intranethash
58 11     11   92 );
  11         22  
59              
60             sub new {
61 71     71 1 72179 my OSPF::LSDB::View6 $self = OSPF::LSDB::new(@_);
62 71 50       180 die "$_[0] does not support IPv4" unless $self->ipv6();
63 71         152 return $self;
64             }
65              
66             ########################################################################
67             # RFC 2740
68             # LSA function code LS Type Description
69             # ----------------------------------------------------
70             # 1 0x2001 Router-LSA
71             ########################################################################
72             # routers => [
73             # area => 'ipv4',
74             # bits => {
75             # B => 'int', # bit B
76             # E => 'int', # bit E
77             # V => 'int', # bit V
78             # W => 'int', # bit W
79             # },
80             # pointtopoints => [] # Point-to-point connection to another router
81             # transits => [] # Connection to a transit network
82             # virtuals => [] # Virtual link
83             # router => 'ipv4', # Link State ID
84             # routerid => 'ipv4', # Advertising Router
85             # ],
86             ########################################################################
87             # $routehash{$routerid} = {
88             # graph => { N => router10, color => red, style => solid, }
89             # hashes => [ { router hash } ]
90             # areas => { $area => 1 }
91             # missing => 1 (optional)
92             # }
93             ########################################################################
94              
95             # take router hash
96             # detect inconsistencies and set colors
97             sub check_router {
98 71     71 0 87 my OSPF::LSDB::View6 $self = shift;
99 71 50       133 my $routehash = $self->{routehash} or die "Uninitialized member";
100 71         201 while (my($rid,$rv) = each %$routehash) {
101 146         156 my %colors;
102 146         145 my @areas = sort keys %{$rv->{areas}};
  146         358  
103 146 100       266 if (@areas > 1) {
104 26         45 $colors{black} = \@areas;
105 26 100       34 if (my @badareas = map { $_->{area} || () }
  3 100       29  
106 53         131 grep { ! $_->{bits}{B} } @{$rv->{hashes}}) {
  26         41  
107             $self->error($colors{orange} =
108 1         7 "Router $rid in multiple areas is not border router ".
109             "in areas @badareas.");
110             }
111             } else {
112 120         191 $colors{gray} = $areas[0];
113             }
114 146 100       218 if ($rv->{missing}) {
115 12         43 $self->error($colors{red} = "Router $rid missing.");
116             } else {
117 134         153 while (my($area,$av) = each %{$rv->{areas}}) {
  295         584  
118             # TODO check wether bits are equal
119 161         390 while (my($lsid,$num) = each %$av) {
120 161 100       405 if ($num > 1) {
121             $self->error($colors{magenta} =
122 1         24 "Router $rid has multiple link state IDs $lsid ".
123             "in area $area.");
124             }
125             }
126             }
127             }
128 146         434 $rv->{colors} = \%colors;
129             }
130             }
131              
132             # take router hash, routerid,
133             # network hash, summary hash, boundary hash, external hash
134             # add missing routers to router hash
135             sub add_missing_router {
136 71     71 0 87 my OSPF::LSDB::View6 $self = shift;
137 71         102 my($index) = @_;
138 71         80 my %rid2areas;
139 71 50       129 my $nethash = $self->{nethash} or die "Uninitialized member";
140 54         57 my @hashes = map { @{$_->{hashes}} } map { values %$_ }
  54         156  
  53         90  
141 71         160 map { values %$_ } values %$nethash;
  58         124  
142 71         124 foreach my $n (@hashes) {
143 56         68 my $area = $n->{area};
144 56         104 $rid2areas{$n->{routerid}}{$area} = 1;
145 56         61 foreach (@{$n->{attachments}}) {
  56         91  
146 109         208 $rid2areas{$_->{routerid}}{$area} = 1;
147             }
148             }
149 71         107 my $intraroutehash = $self->{intraroutehash};
150 71         128 @hashes = map { @{$_->{hashes}} } map { values %$_ }
  4         4  
  4         7  
  3         8  
151             values %$intraroutehash;
152 71         109 foreach my $ir (@hashes) {
153 4         7 my $area = $ir->{area};
154 4         7 $rid2areas{$ir->{router}}{$area} = 1;
155             }
156 71         84 my $lnkhash = $self->{lnkhash};
157 4         8 @hashes = map { @{$_->{hashes}} } map { values %$_ }
  4         5  
  4         6  
158 71         110 map { values %$_ } values %$lnkhash;
  4         8  
159 71         91 foreach my $l (@hashes) {
160 4         5 my $area = $l->{area};
161 4         8 $rid2areas{$l->{routerid}}{$area} = 1;
162             }
163 71         184 $self->add_missing_router_common($index, %rid2areas);
164             }
165              
166             ########################################################################
167             # RFC 2740
168             # Type Description
169             # ---------------------------------------------------
170             # 1 Point-to-point connection to another router
171             ########################################################################
172             # pointtopoints => [
173             # address => 'ipv4', # Neighbor Interface ID
174             # interface => 'ipv4', # Interface ID
175             # metric => 'int', # Metric
176             # routerid => 'ipv4', # Neighbor Router ID
177             # ]
178             ########################################################################
179             # $pointtopointhash{$dst_routerid}{$areas}{$routerid} = {
180             # hashes => [ { link hash } ]
181             # }
182             ########################################################################
183              
184             ########################################################################
185             # RFC 2740
186             # Type Description
187             # ---------------------------------------------------
188             # 4 Virtual link
189             ########################################################################
190             # virtuals => [
191             # address => 'ipv4', # Neighbor Interface ID
192             # interface => 'ipv4', # Interface ID
193             # metric => 'int', # Metric
194             # routerid => 'ipv4', # Neighbor Router ID
195             # ],
196             ########################################################################
197             # $virtualhash{$dst_routerid}{$areas}{$routerid} = {
198             # hashes => [ { link hash } ]
199             # }
200             ########################################################################
201              
202             # take link hash, type (pointtopoint or virtual), router hash
203             # return list of edges from src router to dst router
204             sub router2edges {
205 142     142 0 162 my OSPF::LSDB::View6 $self = shift;
206 142         193 my($type) = @_;
207 142 100       233 my $name = $type eq "pointtopoint" ? "Point-to-point" : "Virtual";
208 142 100       204 my $style = $type eq "pointtopoint" ? "solid" : "dotted";
209 142 50       246 my $routehash = $self->{routehash} or die "Uninitialized member";
210 142 50       296 my $linkhash = $self->{$type."hash"} or die "Uninitialized member";
211 142         166 my $ifaddrs = $self->{ifaddrs};
212 142         144 my @elements;
213 142         145 my $index = 0;
214 142         256 foreach my $dstrid (sort keys %$linkhash) {
215 22         29 my $dv = $linkhash->{$dstrid};
216 22         39 foreach my $area (sort keys %$dv) {
217 22         24 my $ev = $dv->{$area};
218 22         43 foreach my $rid (sort keys %$ev) {
219 22         33 my $rv = $ev->{$rid};
220 22         38 my %colors = (gray => $area);
221 22         35 my $src = $routehash->{$rid}{graph}{N};
222 22         29 my $dst = $routehash->{$dstrid}{graph}{N};
223 22         24 my @hashes = @{$rv->{hashes}};
  22         35  
224 22 100 100     70 if ($type ne "pointtopoint" && @hashes > 1) {
225             $self->error($colors{yellow} =
226 2         12 "$name link at router $rid to router $dstrid ".
227             "has multiple entries in area $area.");
228             }
229 22 100 100     170 if (! $routehash->{$dstrid}{areas}{$area}) {
    100          
230             $self->error($colors{orange} =
231 4         24 "$name link at router $rid to router $dstrid ".
232             "not in same area $area.");
233             } elsif (! ($linkhash->{$rid} && $linkhash->{$rid}{$area} &&
234             $linkhash->{$rid}{$area}{$dstrid}) &&
235             ! $routehash->{$dstrid}{missing}) {
236             $self->error($colors{brown} =
237 2         13 "$name link at router $rid to router $dstrid ".
238             "not symmetric in area $area.");
239             }
240 22         37 foreach my $link (@hashes) {
241 25         38 my $intf = $link->{interface};
242 25         29 delete $colors{green};
243 25 100 66     76 if ($type eq "pointtopoint" and $ifaddrs->{$intf} &&
      100        
244             $ifaddrs->{$intf}{$rid} > 1) {
245             $self->error($colors{green} =
246 1         21 "$name link at router $rid to router $dstrid ".
247             "interface address $intf not unique.");
248             }
249 25         34 my $metric = $link->{metric};
250 25         172 push @elements, {
251             graph => {
252             S => $src,
253             D => $dst,
254             label => $intf,
255             style => $style,
256             taillabel => $metric,
257             },
258             colors => { %colors },
259             index => $index++,
260             };
261             }
262             }
263             }
264             }
265 142         276 return $self->elements2graphs(@elements);
266             }
267              
268             ########################################################################
269             # RFC 2740
270             # Type Description
271             # ---------------------------------------------------
272             # 2 Connection to a transit network
273             ########################################################################
274             # transits => [
275             # address => 'ipv4', # Neighbor Interface ID
276             # interface => 'ipv4', # Interface ID
277             # metric => 'int', # Metric
278             # routerid => 'ipv4', # Neighbor Router ID
279             # ],
280             ########################################################################
281             # $transithash{$address}{$netrouterid}{$area}{$routerid} = {
282             # graph => { N => transit2, color => red, style => solid, } (optional)
283             # hashes => [ { link hash } ]
284             # }
285             # $transitnets->{$interface}{$routerid}{$area}{$address}{$netrouterid}++;
286             ########################################################################
287              
288             # take transit hash, transit cluster hash, net hash
289             # detect inconsistencies and set colors
290             sub check_transit {
291 71     71 0 90 my OSPF::LSDB::View6 $self = shift;
292 71         93 my($transitcluster) = @_;
293 71 50       126 my $nethash = $self->{nethash} or die "Uninitialized member";
294 71 50       124 my $transithash = $self->{transithash} or die "Uninitialized member";
295 71         168 foreach my $addr (sort keys %$transithash) {
296 56         69 my $av = $transithash->{$addr};
297             # TODO check if the there is more than one designated neigbor
298 56         110 foreach my $netrid (sort keys %$av) {
299 56         64 my $nv = $av->{$netrid};
300 56         56 my %colors;
301 56 100 100     137 if (! $nethash->{$addr}{$netrid} &&
302             keys %$nv > 1) {
303             $self->error($colors{orange} =
304 2         9 "Transit network $addr\@$netrid missing in multiple areas.");
305             }
306 56         109 foreach my $area (sort keys %$nv) {
307 60         67 my $ev = $nv->{$area};
308 60         87 $colors{gray} = $area;
309 60         69 delete $colors{blue};
310 60 100 100     118 if (! $nethash->{$addr}{$netrid} && keys %$ev > 1) {
311             $self->error($colors{blue} =
312 1         10 "Transit network $addr\@$netrid missing in area $area ".
313             "at multiple routers.");
314             }
315 60         129 foreach my $rid (sort keys %$ev) {
316 104         126 my $rv = $ev->{$rid};
317 104 100       238 next unless $rv->{graph};
318 9         15 delete @colors{qw(yellow red)};
319 9 100 100     37 if ($nethash->{$addr}{$netrid}) {
    100          
320             $self->error($colors{yellow} =
321 1         9 "Transit network $addr\@$netrid in area $area ".
322             "at router $rid and network not in same area.");
323             } elsif (! $colors{orange} && ! $colors{blue}) {
324             $self->error($colors{red} =
325 2         10 "Transit network $addr\@$netrid network missing.");
326             }
327 9         16 %{$rv->{colors}} = %colors;
  9         19  
328 9         10 push @{$transitcluster->{$addr}}, $rv->{graph};
  9         30  
329             }
330             }
331             }
332             }
333             }
334              
335             # take transit hash, router id, area, link structure, network hash
336             # add new element to transit hash
337             sub add_transit_value {
338 106     106 0 119 my OSPF::LSDB::View6 $self = shift;
339 106         170 my($transithash, $transitnets, $index, $rid, $area, $link) = @_;
340 106 50       192 my $nethash = $self->{nethash} or die "Uninitialized member";
341 106         133 my $addr = $link->{address};
342 106         123 my $netrid = $link->{routerid};
343 106         143 my $intf = $link->{interface};
344 106         339 $transitnets->{$intf}{$rid}{$area}{$addr}{$netrid}++;
345 106         194 my $elem = $transithash->{$addr}{$netrid}{$area}{$rid};
346 106 100       138 if (! $elem) {
347 104         201 $transithash->{$addr}{$netrid}{$area}{$rid} = $elem = {};
348             # check if address is in nethash and in matching nethash area
349 104 100 100     342 if (! $nethash->{$addr}{$netrid} ||
350             ! $nethash->{$addr}{$netrid}{$area}) {
351             $elem->{graph} = {
352 9         55 N => "transitnet$$index",
353             label => "$addr\\n$netrid",
354             shape => "ellipse",
355             style => "dotted",
356             };
357 9         22 $elem->{index} = $$index++;
358             }
359             }
360 106         116 push @{$elem->{hashes}}, $link;
  106         272  
361             }
362              
363             # take hash containing transit network nodes
364             # return list of nodes
365             sub transit2nodes {
366 71     71 0 84 my OSPF::LSDB::View6 $self = shift;
367 71 50       127 my $transithash = $self->{transithash} or die "Uninitialized member";
368 60         117 return $self->elements2graphs(map { values %$_ } map { values %$_ }
  56         87  
369 71         129 map { values %$_ } values %$transithash);
  56         94  
370             }
371              
372             # take link hash, router hash, network hash
373             # return list of edges from router to transit network
374             sub transit2edges {
375 71     71 0 89 my OSPF::LSDB::View6 $self = shift;
376 71 50       131 my $nethash = $self->{nethash} or die "Uninitialized member";
377 71 50       120 my $routehash = $self->{routehash} or die "Uninitialized member";
378 71 50       129 my $transithash = $self->{transithash} or die "Uninitialized member";
379 71         76 my $ifaddrs = $self->{ifaddrs};
380 71         73 my @elements;
381 71         79 my $index = 0;
382 71         131 foreach my $addr (sort keys %$transithash) {
383 56         69 my $av = $transithash->{$addr};
384 56         86 foreach my $netrid (sort keys %$av) {
385 56         70 my $nv = $av->{$netrid};
386 56         83 my $nid = "$addr\@$netrid";
387 56         88 foreach my $area (sort keys %$nv) {
388 60         66 my $ev = $nv->{$area};
389 60         96 foreach my $rid (sort keys %$ev) {
390 104         131 my $rv = $ev->{$rid};
391 104         161 my %colors = (gray => $area);
392 104         147 my $src = $routehash->{$rid}{graph}{N};
393 104 100       105 if (@{$rv->{hashes}} > 1) {
  104         178  
394             $self->error($colors{yellow} =
395 2         18 "Transit network $nid at router $rid ".
396             "has multiple entries in area $area.");
397             }
398 104         125 foreach my $link (@{$rv->{hashes}}) {
  104         163  
399 106         128 my $intf = $link->{interface};
400 106         115 delete $colors{green};
401 106 100 66     297 if ($ifaddrs->{$intf} && $ifaddrs->{$intf}{$rid} > 1) {
402             $self->error($colors{green} =
403 3         18 "Transit link at router $rid to network $nid ".
404             "interface address $intf not unique.");
405             }
406 106         138 my $metric = $link->{metric};
407             # link from designated router to attached net
408 106 100 100     231 my $style = $netrid eq $rid && $addr eq $intf ?
409             "bold" : "solid";
410 106         136 delete $colors{magenta};
411 106         104 delete $colors{brown};
412 106         109 delete $colors{tan};
413 106 100       157 if ($rv->{graph}) {
414 10         12 my $dst = $rv->{graph}{N};
415 10         46 push @elements, {
416             graph => {
417             S => $src,
418             D => $dst,
419             headlabel => $intf,
420             style => $style,
421             taillabel => $metric,
422             },
423             colors => { %colors },
424             index => $index++,
425             };
426 10         32 next;
427             }
428 96         115 my $nv = $nethash->{$addr}{$netrid};
429 96         105 delete $colors{magenta};
430 96 50       160 my $ev = $nv->{$area}
431             or next;
432 96         99 delete $colors{brown};
433 96         91 delete $colors{tan};
434 96 100       152 if (! $ev->{attachrouters}{$rid}) {
435             $self->error($colors{brown} =
436 2         14 "Transit link at router $rid not attached ".
437             "by network $nid in area $area.");
438             }
439 96         114 my $dst = $ev->{graph}{N};
440 96         604 push @elements, {
441             graph => {
442             S => $src,
443             D => $dst,
444             headlabel => $intf,
445             style => $style,
446             taillabel => $metric,
447             },
448             colors => { %colors },
449             index => $index++,
450             };
451             }
452             }
453             }
454             }
455             }
456 71         156 return $self->elements2graphs(@elements);
457             }
458              
459             ########################################################################
460             # RFC 2740
461             # LSA function code LS Type Description
462             # ----------------------------------------------------
463             # 2 0x2002 Network-LSA
464             ########################################################################
465             # networks => [
466             # address => 'ipv4', # Link State ID
467             # area => 'ipv4',
468             # attachments => [
469             # routerid => 'ipv4', # Attached Router
470             # ],
471             # routerid => 'ipv4', # Advertising Router
472             # ],
473             ########################################################################
474             # $nethash{$address}{$routerid}{$area} = {
475             # graph => { N => network1, color => red, style => bold, }
476             # hashes => [ { network hash } ]
477             # attachrouters => { $attachmentrouterid => 1 }
478             # }
479             # $nets{$address}{$routerid}++
480             # $netareas{$address}{$routerid}{$area}++
481             ########################################################################
482              
483             # take network hash, net cluster hash, net hash
484             # detect inconsistencies and set colors
485             sub check_network {
486 71     71 0 88 my OSPF::LSDB::View6 $self = shift;
487 71         96 my($netcluster) = @_;
488 71 50       139 my $nethash = $self->{nethash} or die "Uninitialized member";
489 71 50       112 my $nets = $self->{nets} or die "Uninitialized member";
490 71         73 my %colors;
491 71         154 foreach my $addr (sort keys %$nethash) {
492 58         71 my $av = $nethash->{$addr};
493 58         90 foreach my $rid (sort keys %$av) {
494 53         63 my $rv = $av->{$rid};
495 53         88 my $nid = "$addr\@$rid";
496 53         60 delete $colors{green};
497 53 100       98 if ($nets->{$addr}{$rid} > 1) {
498             $self->error($colors{green} =
499 3         31 "Network $nid not unique at router $rid.");
500             }
501 53         58 delete $colors{orange};
502 53 100       105 if (keys %$rv > 1) {
503             $self->error($colors{orange} =
504 1         5 "Network $nid at router $rid in multiple areas.");
505             }
506 53         87 foreach my $area (sort keys %$rv) {
507 54         73 my $ev = $rv->{$area};
508 54 100       79 if ($ev->{missing}) {
509 1         5 $self->error($colors{red} = "Network $nid missing ".
510             "in area $area.");
511             } else {
512 53         81 $colors{gray} = $area;
513 53         65 delete $colors{yellow};
514 53 100       51 if (@{$ev->{hashes}} > 1) {
  53         90  
515             $self->error($colors{yellow} =
516 2         10 "Network $nid at router $rid ".
517             "has multiple entries in area $area.");
518             }
519 53         60 delete $colors{brown};
520 53         53 my @attrids = keys %{$ev->{attachrouters}};
  53         126  
521 53 100       111 if (@attrids == 0) {
522             $self->error($colors{red} =
523 1         7 "Network $nid at router $rid not attached ".
524             "to any router in area $area.");
525             }
526 53 100       100 if (@attrids == 1) {
527             $self->error($colors{brown} =
528 1         8 "Network $nid at router $rid attached only ".
529             "to router @attrids in area $area.");
530             }
531             }
532 54         81 %{$ev->{colors}} = %colors;
  54         108  
533             # TODO move netcluster to prefix lsa
534 54         60 push @{$netcluster->{"$addr\@$rid"}}, $ev->{graph};
  54         211  
535             }
536             }
537             }
538             }
539              
540             # take network structure, net cluster hash
541             # return network hash
542             sub create_network {
543 71     71 0 86 my OSPF::LSDB::View6 $self = shift;
544 71         108 my($index) = @_;
545 71         120 my %nethash;
546             my %nets;
547 71         0 my %netareas;
548 71         89 foreach my $n (@{$self->{ospf}{database}{networks}}) {
  71         174  
549 55         82 my $addr = $n->{address};
550 55         61 my $rid = $n->{routerid};
551 55         91 my $nid = "$addr\@$rid";
552 55         106 $nets{$addr}{$rid}++;
553 55         65 my $area = $n->{area};
554 55         102 $netareas{$addr}{$rid}{$area}++;
555 55         98 my $elem = $nethash{$addr}{$rid}{$area};
556 55 100       83 if (! $elem) {
557 53         103 $nethash{$addr}{$rid}{$area} = $elem = {};
558             $elem->{graph} = {
559 53         208 N => "network$$index",
560             label => "$addr\\n$rid",
561             shape => "ellipse",
562             style => "bold",
563             };
564 53         137 $elem->{index} = $$index++;
565             }
566 55         61 push @{$elem->{hashes}}, $n;
  55         110  
567 55         62 foreach my $att (@{$n->{attachments}}) {
  55         78  
568 109         232 $elem->{attachrouters}{$att->{routerid}} = 1;
569             }
570             }
571 71         113 $self->{nethash} = \%nethash;
572 71         91 $self->{nets} = \%nets;
573             # TODO netareas should handle prefixes
574 71         143 $self->{netareas} = \%netareas;
575             }
576              
577             # take network hash,
578             # intra network hash
579             # add missing networks to network hash
580             sub add_missing_network {
581 71     71 0 91 my OSPF::LSDB::View6 $self = shift;
582 71         89 my($index) = @_;
583 71         100 my $intranethash = $self->{intranethash};
584 71 50       128 my $nethash = $self->{nethash} or die "Uninitialized member";
585 71 50       104 my $nets = $self->{nets} or die "Uninitialized member";
586 71 50       106 my $netareas = $self->{netareas} or die "Uninitialized member";
587 71         240 foreach my $addr (sort keys %$intranethash) {
588 3         6 my $av = $intranethash->{$addr};
589 3         10 foreach my $rid (sort keys %$av) {
590 3         3 my $rv = $av->{$rid};
591 3         8 foreach my $area (sort keys %$rv) {
592 3         4 my $ev = $rv->{$area};
593 3         13 my $elem = $nethash->{$addr}{$rid}{$area};
594 3 100       5131 if (! $elem) {
595 1         2 $nets->{$addr}{$rid}++;
596 1         3 $netareas->{$addr}{$rid}{$area}++;
597 1         2 $nethash->{$addr}{$rid}{$area} = $elem = {};
598             $elem->{graph} = {
599 1         6 N => "network$$index",
600             label => "$addr\\n$rid",
601             shape => "ellipse",
602             style => "dotted",
603             };
604 1         3 $elem->{index} = $$index++;
605 1         2 push @{$elem->{hashes}}, {
  1         3  
606             area => $area,
607             routerid => $rid,
608             };
609 1         4 $elem->{missing}++;
610             }
611             }
612             }
613             }
614             }
615              
616             # take hash containing network nodes
617             # return list of nodes
618             sub network2nodes {
619 71     71 0 93 my OSPF::LSDB::View6 $self = shift;
620 71 50       120 my $nethash = $self->{nethash} or die "Uninitialized member";
621 71         115 return $self->elements2graphs(map { values %$_ } map { values %$_ }
  53         96  
  58         102  
622             values %$nethash);
623             }
624              
625             # take network hash, router hash
626             # return list of edges from transit network to router
627             sub network2edges {
628 71     71 0 86 my OSPF::LSDB::View6 $self = shift;
629 71 50       123 my $nethash = $self->{nethash} or die "Uninitialized member";
630 71 50       110 my $routehash = $self->{routehash} or die "Uninitialized member";
631 71 50       122 my $transithash = $self->{transithash} or die "Uninitialized member";
632 71         68 my @elements;
633 71         75 my $index = 0;
634 71         136 foreach my $addr (sort keys %$nethash) {
635 58         73 my $av = $nethash->{$addr};
636 58         86 foreach my $rid (sort keys %$av) {
637 53         66 my $rv = $av->{$rid};
638 53         84 my $nid = "$addr\@$rid";
639 53         77 foreach my $area (sort keys %$rv) {
640 54         58 my $ev = $rv->{$area};
641 54         76 my $src = $ev->{graph}{N};
642 54         58 foreach my $net (@{$ev->{hashes}}) {
  54         78  
643 56         61 my %attcolors;
644 56         56 foreach (@{$net->{attachments}}) {
  56         108  
645 109         134 my $arid = $_->{routerid};
646 109 100       173 if ($attcolors{$arid}) {
647             $self->error($attcolors{$arid}{yellow} =
648 2         11 "Network $nid in area $area at router $rid ".
649             "attached to router $arid multiple times.");
650 2         4 next;
651             }
652 107         168 $attcolors{$arid}{gray} = $area;
653 107 100 66     319 if ($routehash->{$arid}{areas} &&
654             ! $routehash->{$arid}{areas}{$area}) {
655             $self->error($attcolors{$arid}{orange} =
656 4         22 "Network $nid and router $arid ".
657             "not in same area $area.");
658 4         8 next;
659             }
660 103         149 my $tv = $transithash->{$addr}{$rid}{$area}{$arid};
661 103 100 100     169 if (! $tv && ! $routehash->{$arid}{missing}) {
662             $self->error($attcolors{$arid}{brown} =
663 1         10 "Network $nid not transit net ".
664             "of attached router $arid in area $area.");
665 1         3 next;
666             }
667 102 100 100     255 if ($arid eq $rid && $tv && ! grep { $addr eq
      100        
668 47         162 $_->{interface} } @{$tv->{hashes}}) {
  46         74  
669             $self->error($attcolors{$arid}{tan} =
670 5         35 "Network $nid at router $arid in area $area ".
671             "is designated but transit link is not.");
672 5         10 next;
673             }
674             }
675 56         75 foreach (@{$net->{attachments}}) {
  56         99  
676 109         154 my $arid = $_->{routerid};
677             my $dst = $routehash->{$arid}{graph}{N}
678 109 50       213 or die "No router graph $arid";
679 109         115 my $style = "solid";
680 109 100       161 if ($arid eq $rid) {
681             # router is designated router
682 54         64 $style = "bold";
683             }
684             push @elements, {
685             graph => {
686             S => $src,
687             D => $dst,
688             style => $style,
689             },
690 109         209 colors => { %{$attcolors{$arid}} },
  109         375  
691             index => $index++,
692             };
693             }
694 56 100       186 if (! $attcolors{$rid}) {
695             my $dst = $routehash->{$rid}{graph}{N}
696 4 50       18 or die "No router graph $rid";
697 4         15 $attcolors{$rid}{gray} = $area;
698             $self->error($attcolors{$rid}{red} =
699 4         21 "Network $nid not attached ".
700             "to designated router $rid in area $area.");
701             push @elements, {
702             graph => {
703             S => $src,
704             D => $dst,
705             style => "bold",
706             },
707 4         26 colors => { %{$attcolors{$rid}} },
  4         26  
708             index => $index++,
709             };
710             }
711             }
712             }
713             }
714             }
715 71         154 return $self->elements2graphs(@elements);
716             }
717              
718             ########################################################################
719             # RFC 2740
720             # LSA function code LS Type Description
721             # ----------------------------------------------------
722             # 3 0x2003 Inter-Area-Prefix-LSA
723             ########################################################################
724             # summarys => [
725             # address => 'ipv4', # Link State ID
726             # area => 'ipv4',
727             # metric => 'int', # Metric
728             # prefixaddress => 'ipv6', # Address Prefix
729             # prefixlength => 'int', # PrefixLength
730             # routerid => 'ipv4', # Advertising Router
731             # ],
732             ########################################################################
733             # $sumhash{$prefixaddress}{$prefixlength} = {
734             # graph => { N => summary4, color => red, style => solid, }
735             # hashes => [ { summary hash } ]
736             # arearids => { $area => { $routerid => 1 } }
737             # }
738             ########################################################################
739              
740             # take summary hash, net cluster hash, network hash, stub hash
741             # detect inconsistencies and set colors
742             sub check_summary {
743 52     52 0 57 my OSPF::LSDB::View6 $self = shift;
744 52         82 my($netcluster) = @_;
745 52 50       85 my $netareas = $self->{netareas} or die "Uninitialized member";
746 52 50       90 my $sumhash = $self->{sumhash} or die "Uninitialized member";
747 52         117 foreach my $paddr (sort keys %$sumhash) {
748 9         11 my $av = $sumhash->{$paddr};
749 9         17 foreach my $plen (sort keys %$av) {
750 9         12 my $lv = $av->{$plen};
751 9         11 my %colors;
752 9         11 my $nid = "$paddr/$plen";
753 9         11 my @areas = sort keys %{$lv->{arearids}};
  9         23  
754 9 100       19 if (@areas > 1) {
755 5         9 $colors{black} = \@areas;
756             } else {
757 4         7 $colors{gray} = $areas[0];
758             }
759             # TODO check wether lower prefix address is zero
760             # TODO check Link- and Prefix-LSAs
761             # if (my @badareas = grep { $netareas->{$net}{$mask}{$_} } @areas) {
762             # $self->error($colors{blue} =
763             # "Summary network $nid is also network in areas @badareas.");
764             # }
765             # if ($stubareas and
766             # my @badareas = grep { $stubareas->{$net}{$mask}{$_} } @areas) {
767             # $self->error($colors{green} =
768             # "Summary network $nid is also stub network ".
769             # "in areas @badareas.");
770             # }
771             # TODO check for duplicate Link-State-IDs
772 9         23 $lv->{colors} = \%colors;
773 9         11 push @{$netcluster->{"$paddr/$plen"}}, $lv->{graph};
  9         33  
774             }
775             }
776             }
777              
778             # take summary structure, net cluster hash, network hash, link hash
779             # return summary hash
780             sub create_summary {
781 52     52 0 72 my OSPF::LSDB::View6 $self = shift;
782 52         57 my $index = 0;
783 52         85 my %sumhash;
784             my %sums;
785 52         0 my %sumlsids;
786 52         54 foreach my $s (@{$self->{ospf}{database}{summarys}}) {
  52         107  
787 17         30 my $paddr = $s->{prefixaddress};
788 17         20 my $plen = $s->{prefixlength};
789 17         25 my $nid = "$paddr/$plen";
790 17         21 my $rid = $s->{routerid};
791 17         19 my $addr = $s->{address};
792 17         18 my $area = $s->{area};
793 17         36 $sumlsids{$area}{$rid}{$addr}++;
794 17         28 my $elem = $sumhash{$paddr}{$plen};
795 17 100       28 if (! $elem) {
796 9         18 $sumhash{$paddr}{$plen} = $elem = {};
797             $elem->{graph} = {
798 9         39 N => "summary$index",
799             label => "$paddr/$plen",
800             shape => "ellipse",
801             style => "dashed",
802             };
803 9         23 $elem->{index} = $index++;
804             }
805 17         20 push @{$elem->{hashes}}, $s;
  17         25  
806 17         45 $elem->{arearids}{$area}{$rid}++;
807             }
808 52         77 $self->{sumhash} = \%sumhash;
809 52         64 $self->{sums} = \%sums;
810 52         90 $self->{sumlsids} = \%sumlsids;
811             }
812              
813             # take summary hash, router hash
814             # return list of edges from summary network to router
815             sub summary2edges {
816 52     52 0 65 my OSPF::LSDB::View6 $self = shift;
817 52 50       87 my $routehash = $self->{routehash} or die "Uninitialized member";
818 52 50       85 my $sumhash = $self->{sumhash} or die "Uninitialized member";
819 52 50       83 my $sumlsids = $self->{sumlsids} or die "Uninitialized member";
820 52         63 my @elements;
821 52         59 my $index = 0;
822 52         89 foreach my $paddr (sort keys %$sumhash) {
823 9         13 my $av = $sumhash->{$paddr};
824 9         16 foreach my $plen (sort keys %$av) {
825 9         9 my $lv = $av->{$plen};
826 9         17 my $nid = "$paddr/$plen";
827 9   33     32 my $src = $lv->{graph} && $lv->{graph}{N};
828 9         12 foreach my $s (@{$lv->{hashes}}) {
  9         16  
829 17         21 my $rid = $s->{routerid};
830             my $dst = $routehash->{$rid}{graph}{N}
831 17 50       31 or die "No router graph $rid";
832 17         21 my $addr = $s->{address};
833 17         22 my $area = $s->{area};
834 17         29 my %colors = (gray => $area);
835 17 100       34 if (! $routehash->{$rid}{areas}{$area}) {
836             $self->error($colors{orange} =
837 1         17 "Summary network $nid and router $rid ".
838             "not in same area $area.");
839             }
840 17 100       33 if ($lv->{arearids}{$area}{$rid} > 1) {
841             $self->error($colors{yellow} =
842 4         15 "Summary network $nid at router $rid ".
843             "has multiple entries in area $area.");
844             }
845 17 100       32 if ($sumlsids->{$area}{$rid}{$addr} > 1) {
846             $self->error($colors{magenta} =
847 4         16 "Summary network $nid at router $rid ".
848             "has multiple link state IDs $addr in area $area.");
849             }
850 17         19 my $metric = $s->{metric};
851             $s->{graph} = {
852 17         52 S => $src,
853             D => $dst,
854             headlabel => $metric,
855             style => "dashed",
856             taillabel => $addr,
857             };
858 17         37 $s->{colors} = \%colors;
859 17         25 $s->{index} = $index++;
860             # in case of aggregation src is undef
861 17 50       46 push @elements, $s if $src;
862             }
863             }
864             }
865 52         89 return $self->elements2graphs(@elements);
866             }
867              
868             ########################################################################
869             # RFC 2740
870             # LSA function code LS Type Description
871             # ----------------------------------------------------
872             # 4 0x2004 Inter-Area-Router-LSA
873             ########################################################################
874             # boundarys => [
875             # address => 'ipv4', # Link State ID
876             # area => 'ipv4',
877             # asbrouter => 'ipv4', # Destination Router ID
878             # metric => 'int', # Metric
879             # routerid => 'ipv4', # Advertising Router
880             # ],
881             ########################################################################
882             # $boundhash{$asbrouter} = {
883             # graph => { N => boundary6, color => red, style => dashed, }
884             # hashes => [ { boundary hash } ]
885             # arearids => { $area => { $routerid => 1 }
886             # aggregate => { $asbraggr => 1 } (optional)
887             # }
888             ########################################################################
889              
890             # take boundary structure
891             # return boundary hash
892             sub create_boundary {
893 52     52 0 67 my OSPF::LSDB::View6 $self = shift;
894 52         57 my $index = 0;
895 52         59 my %boundhash;
896             my %boundlsids;
897 52         54 foreach my $b (@{$self->{ospf}{database}{boundarys}}) {
  52         98  
898 32         43 my $asbr = $b->{asbrouter};
899 32         37 my $rid = $b->{routerid};
900 32         39 my $area = $b->{area};
901 32         39 my $addr = $b->{address};
902 32         60 $boundlsids{$area}{$rid}{$addr}++;
903 32         41 my $elem = $boundhash{$asbr};
904 32 100       58 if (! $elem) {
905 20         33 $boundhash{$asbr} = $elem = {};
906             $elem->{graph} = {
907 20         65 N => "boundary$index",
908             label => $asbr,
909             shape => "box",
910             style => "dashed",
911             };
912 20         38 $elem->{index} = $index++;
913             }
914 32         35 push @{$elem->{hashes}}, $b;
  32         49  
915 32         64 $elem->{arearids}{$area}{$rid}++;
916             }
917 52         72 $self->{boundhash} = \%boundhash;
918 52         93 $self->{boundlsids} = \%boundlsids;
919             }
920              
921             # take boundary hash, router hash
922             # return list of edges from boundary router to router
923             sub boundary2edges {
924 52     52 0 57 my OSPF::LSDB::View6 $self = shift;
925 52 50       96 my $routehash = $self->{routehash} or die "Uninitialized member";
926 52 50       99 my $boundhash = $self->{boundhash} or die "Uninitialized member";
927 52 50       80 my $boundlsids = $self->{boundlsids} or die "Uninitialized member";
928 52         54 my @elements;
929 52         53 my $index = 0;
930 52         88 foreach my $asbr (sort keys %$boundhash) {
931 20         27 my $bv = $boundhash->{$asbr};
932 20         28 my $src;
933 20 100       43 if ($bv->{graph}) {
    50          
934 13         27 $src = $bv->{graph}{N};
935             } elsif ($routehash->{$asbr}) {
936             $src = $routehash->{$asbr}{graph}{N}
937 7         16 }
938 20         24 foreach my $b (@{$bv->{hashes}}) {
  20         33  
939 32         41 my $rid = $b->{routerid};
940             my $dst = $routehash->{$rid}{graph}{N}
941 32 50       53 or die "No router graph $rid";
942 32         41 my $addr = $b->{address};
943 32         59 my $area = $b->{area};
944 32         57 my %colors = (gray => $area);
945 32 100 66     114 if ($asbr eq $rid) {
    100          
946             $self->error($colors{brown} =
947 1         6 "AS boundary router $asbr is advertized by itself ".
948             "in area $area.");
949             } elsif ($routehash->{$asbr} && $routehash->{$asbr}{areas}{$area}) {
950             $self->error($colors{blue} =
951 1         6 "AS boundary router $asbr is router in same area $area.");
952             }
953 32 100       65 if (! $routehash->{$rid}{areas}{$area}) {
954             $self->error($colors{orange} =
955 2         11 "AS boundary router $asbr and router $rid ".
956             "not in same area $area.");
957             }
958 32 100       62 if ($bv->{arearids}{$area}{$rid} > 1) {
959             $self->error($colors{yellow} =
960 6         22 "AS boundary router $asbr at router $rid ".
961             "has multiple entries in area $area.");
962             }
963 32 100       59 if ($boundlsids->{$area}{$rid}{$addr} > 1) {
964             $self->error($colors{magenta} =
965 4         23 "AS boundary router $asbr at router $rid ".
966             "has multiple link state IDs $addr in area $area.");
967             }
968 32         43 my $metric = $b->{metric};
969             $b->{graph} = {
970 32         100 S => $src,
971             D => $dst,
972             headlabel => $metric,
973             style => "dashed",
974             taillabel => $addr,
975             };
976 32         50 $b->{colors} = \%colors;
977 32         56 $b->{index} = $index++;
978             # in case of aggregation src is undef
979 32 50       75 push @elements, $b if $src;
980             }
981             }
982 52         106 return $self->elements2graphs(@elements);
983             }
984              
985             ########################################################################
986             # RFC 2740
987             # LSA function code LS Type Description
988             # ----------------------------------------------------
989             # 5 0x4005 AS-External-LSA
990             ########################################################################
991             # externals => [
992             # address => 'ipv4', # Link State ID
993             # metric => 'int', # Metric
994             # prefixaddress => 'ipv6', # Address Prefix
995             # prefixlength => 'int', # PrefixLength
996             # routerid => 'ipv4', # Advertising Router
997             # type => 'int', # bit E
998             # ],
999             ########################################################################
1000             # $externhash{$prefixaddress}{$prefixlength} = {
1001             # graph => { N => external8, color => red, style => dashed, }
1002             # hashes => [ { ase hash } ]
1003             # routers => { $routerid => 1 }
1004             # }
1005              
1006             # take external hash, net cluster hash, network hash, stub hash, summary hash
1007             # detect inconsistencies and set colors
1008             sub check_external {
1009 52     52 0 61 my OSPF::LSDB::View6 $self = shift;
1010 52         72 my($netcluster) = @_;
1011 52 50       136 my $nets = $self->{nets} or die "Uninitialized member";
1012 52         79 my $sums = $self->{sums};
1013 52 50       84 my $externhash = $self->{externhash} or die "Uninitialized member";
1014 52         127 foreach my $paddr (sort keys %$externhash) {
1015 12         15 my $av = $externhash->{$paddr};
1016 12         18 foreach my $plen (sort keys %$av) {
1017 12         15 my $lv = $av->{$plen};
1018 12         20 my %colors = (gray => "ase");
1019 12         17 my $nid = "$paddr/$plen";
1020             # TODO check wether lower prefix address is zero
1021             # TODO check Link- and Prefix-LSAs
1022             # if ($nets->{$net}{$mask}) {
1023             # $self->error($colors{blue} =
1024             # "AS external network $nid is also network.");
1025             # }
1026             # if ($stubs->{$net}{$mask}) {
1027             # $self->error($colors{green} =
1028             # "AS external network $nid is also stub network.");
1029             # }
1030             # if ($sums->{$net}{$mask}) {
1031             # $self->error($colors{cyan} =
1032             # "AS external network $nid is also summary network.");
1033             # }
1034             # TODO check for duplicate Link-State-IDs
1035 12         15 $lv->{colors} = \%colors;
1036 12         14 push @{$netcluster->{"$paddr/$plen"}}, $lv->{graph};
  12         37  
1037             }
1038             }
1039             }
1040              
1041             # take external structure, net cluster hash, network hash, link hash
1042             # return external hash
1043             sub create_external {
1044 52     52 0 64 my OSPF::LSDB::View6 $self = shift;
1045 52         55 my $index = 0;
1046 52         63 my %externhash;
1047             my %externlsids;
1048 52         63 foreach my $e (@{$self->{ospf}{database}{externals}}) {
  52         98  
1049 24         29 my $paddr = $e->{prefixaddress};
1050 24         27 my $plen = $e->{prefixlength};
1051 24         27 my $rid = $e->{routerid};
1052 24         27 my $addr = $e->{address};
1053 24         37 $externlsids{$rid}{$addr}++;
1054 24         33 my $elem = $externhash{$paddr}{$plen};
1055 24 100       43 if (! $elem) {
1056 12         19 $externhash{$paddr}{$plen} = $elem = {};
1057             $elem->{graph} = {
1058 12         47 N => "external$index",
1059             label => "$paddr/$plen",
1060             shape => "egg",
1061             style => "solid",
1062             };
1063 12         32 $elem->{index} = $index++;
1064             }
1065 24         24 push @{$elem->{hashes}}, $e;
  24         33  
1066 24         42 $elem->{routers}{$rid}++;
1067             }
1068 52         94 $self->{externhash} = \%externhash;
1069 52         92 $self->{externlsids} = \%externlsids;
1070             }
1071              
1072             # take external hash, router hash, boundary hash, boundary aggregate
1073             # return list of edges from external network to router
1074             sub external2edges {
1075 52     52 0 60 my OSPF::LSDB::View6 $self = shift;
1076 52 50       90 my $routehash = $self->{routehash} or die "Uninitialized member";
1077 52         56 my $boundhash = $self->{boundhash};
1078 52         70 my $boundaggr = $self->{boundaggr};
1079 52 50       98 my $externhash = $self->{externhash} or die "Uninitialized member";
1080 52 50       85 my $externlsids = $self->{externlsids} or die "Uninitialized member";
1081 52         55 my @elements;
1082 52         57 my $index = 0;
1083 52         85 foreach my $paddr (sort keys %$externhash) {
1084 12         25 my $pv = $externhash->{$paddr};
1085 12         23 foreach my $plen (sort keys %$pv) {
1086 12         13 my $lv = $pv->{$plen};
1087 12         20 my $nid = "$paddr/$plen";
1088 12         21 my $src = $lv->{graph}{N};
1089 12         13 my %dtm; # when dst is aggregated, aggregate edges
1090 12         15 foreach my $e (@{$lv->{hashes}}) {
  12         20  
1091 24         30 my $rid = $e->{routerid};
1092 24         28 my $addr = $e->{address};
1093 24         39 my $type = $e->{type};
1094 24         26 my $metric = $e->{metric};
1095 24         50 my %colors = (gray => "ase");
1096 24 100       44 if ($lv->{routers}{$rid} > 1) {
1097             $self->error($colors{yellow} =
1098 7         49 "AS external network $nid at router $rid ".
1099             "has multiple entries.");
1100             }
1101 24 100       45 if ($externlsids->{$rid}{$addr} > 1) {
1102             $self->error($colors{magenta} =
1103 7         26 "AS external network $nid at router $rid ".
1104             "has multiple link state IDs $addr.");
1105             }
1106 24 100       47 my $style = $type == 1 ? "solid" : "dashed";
1107 24         65 my %graph = (
1108             S => $src,
1109             headlabel => $metric,
1110             style => $style,
1111             taillabel => $addr,
1112             );
1113 24 100       42 if ($routehash->{$rid}) {
1114             my $dst = $routehash->{$rid}{graph}{N}
1115 15 50       32 or die "No router graph $rid";
1116 15         19 $graph{D} = $dst;
1117 15         61 $e->{elems}{$dst} = {
1118             graph => \%graph,
1119             colors => \%colors,
1120             index => $index++,
1121             };
1122 15 50       36 push @elements, $e->{elems}{$dst} if $src;
1123 15         27 next;
1124             }
1125 9         12 my $av = $boundhash->{$rid}{aggregate};
1126 9 50       17 if (! $av) {
1127             my $dst = $boundhash->{$rid}{graph}{N}
1128 9 50       21 or die "No ASB router graph $rid";
1129 9         12 $graph{D} = $dst;
1130 9         27 $e->{elems}{$dst} = {
1131             graph => \%graph,
1132             colors => \%colors,
1133             index => $index++,
1134             };
1135 9 50       19 push @elements, $e->{elems}{$dst} if $src;
1136 9         16 next;
1137             }
1138 0         0 foreach my $asbraggr (sort keys %$av) {
1139 0         0 my $num = $av->{$asbraggr};
1140             my $dst = $boundaggr->{$asbraggr}{graph}{N}
1141 0 0       0 or die "No ASBR graph $asbraggr";
1142 0         0 $graph{D} = $dst;
1143 0         0 $e->{elems}{$dst} = {
1144             graph => { %graph },
1145             colors => { %colors },
1146             index => $index++,
1147             };
1148             # no not aggregate graphs with errors
1149 0 0       0 if (grep { ! /^(gray|black)$/ } keys %colors) {
  0         0  
1150 0 0       0 push @elements, $e->{elems}{$dst} if $src;
1151             } else {
1152 0         0 $dtm{$dst}{$type}{$metric} = $e->{elems}{$dst};
1153             }
1154             }
1155             }
1156 12 50       37 push @elements, map { values %$_ } map { values %$_ } values %dtm
  0         0  
  0         0  
1157             if $src;
1158             }
1159             }
1160 52         94 return $self->elements2graphs(@elements);
1161             }
1162              
1163             ########################################################################
1164             # $externaggr{$netaggr} = {
1165             # graph => { N => external9, color => red, style => dashed, }
1166             # routers => { $routerid => { $type => { $metric => [ { ase hash } ] } } }
1167             # }
1168             ########################################################################
1169              
1170             # take external hash
1171             # return external aggregate
1172             sub create_externaggr {
1173 0     0 0 0 my OSPF::LSDB::View6 $self = shift;
1174             # $ridnets{$rid}{$network} =
1175             # color => orange,
1176             # types => { $type => { $metric => [ { ase hash } ] } }
1177 0 0       0 my $externhash = $self->{externhash} or die "Uninitialized member";
1178 0         0 my %ridnets;
1179 0         0 foreach my $net (sort keys %$externhash) {
1180 0         0 my $nv = $externhash->{$net};
1181 0         0 foreach my $mask (sort keys %$nv) {
1182 0         0 my $mv = $nv->{$mask};
1183 0         0 my $nid = "$net/$mask";
1184             # no not aggregate clustered graphs
1185 0 0       0 next if $mv->{graph}{C};
1186 0         0 my $colors = $mv->{colors};
1187             # no not aggregate graphs with errors
1188 0 0       0 next if grep { ! /^(gray|black)$/ } keys %$colors;
  0         0  
1189 0         0 foreach my $e (@{$mv->{hashes}}) {
  0         0  
1190 0         0 my $rid = $e->{routerid};
1191 0         0 my $type = $e->{type};
1192 0         0 my $metric = $e->{metric};
1193 0         0 my $elem = $ridnets{$rid}{$nid};
1194 0 0 0     0 if (! $elem) {
    0 0        
1195 0         0 $ridnets{$rid}{$nid} = $elem = {
1196             colors => { %$colors },
1197             };
1198             } elsif (! $elem->{colors}{gray} || ! $colors->{gray} ||
1199             $elem->{colors}{gray} ne $colors->{gray}) {
1200 0         0 push @{$elem->{colors}{black}},
1201             (delete($elem->{colors}{gray}) || ()),
1202 0 0 0     0 ($colors->{gray} || ()), @{$colors->{black} || []};
  0   0     0  
1203             }
1204 0         0 push @{$elem->{types}{$type}{$metric}}, $e;
  0         0  
1205             }
1206 0         0 delete $mv->{graph};
1207             }
1208             }
1209 0         0 my $index = 0;
1210 0         0 my %externaggr;
1211 0         0 foreach my $rid (sort keys %ridnets) {
1212 0         0 my $rv = $ridnets{$rid};
1213 0         0 my $netaggr = join('\n', sort keys %$rv); # TODO ip sort
1214 0         0 my $elem = $externaggr{$netaggr};
1215 0 0       0 if (! $elem) {
1216 0         0 $externaggr{$netaggr} = $elem = {};
1217             $elem->{graph} = {
1218 0         0 N => "externalaggregate$index",
1219             label => $netaggr,
1220             shape => "egg",
1221             style => "solid",
1222             };
1223 0         0 $elem->{index} = $index++;
1224             }
1225 0         0 foreach my $nid (sort keys %$rv) {
1226 0         0 my $nv = $rv->{$nid};
1227 0         0 my $colors = $nv->{colors};
1228 0 0 0     0 if (! $elem->{colors}) {
    0 0        
1229 0         0 %{$elem->{colors}} = %$colors;
  0         0  
1230             } elsif (! $elem->{colors}{gray} || ! $colors->{gray} ||
1231             $elem->{colors}{gray} ne $colors->{gray}) {
1232 0         0 push @{$elem->{colors}{black}},
1233             (delete($elem->{colors}{gray}) || ()),
1234 0 0 0     0 ($colors->{gray} || ()), @{$colors->{black} || []};
  0   0     0  
1235             }
1236 0         0 foreach my $type (sort keys %{$nv->{types}}) {
  0         0  
1237 0         0 my $tv = $nv->{types}{$type};
1238 0         0 foreach my $metric (sort keys %$tv) {
1239 0         0 my $es = $tv->{$metric};
1240 0         0 push @{$elem->{routers}{$rid}{$type}{$metric}}, @$es;
  0         0  
1241             }
1242             }
1243             }
1244             }
1245 0         0 $self->{externaggr} = \%externaggr;
1246             }
1247              
1248             ########################################################################
1249             # RFC 2740
1250             # LSA function code LS Type Description
1251             # ----------------------------------------------------
1252             # 8 0x0008 Link-LSA
1253             ########################################################################
1254             # links => [
1255             # area => 'ipv4',
1256             # interface => 'ipv4', # Link State ID
1257             # linklocal => 'ipv6', # Link-local Interface Address
1258             # prefixes => [
1259             # prefixaddress => 'ipv6', # Address Prefix
1260             # prefixlength => 'int', # PrefixLength
1261             # ],
1262             # routerid => 'ipv4', # Advertising Router
1263             # ],
1264             ########################################################################
1265             # $lnkhash{$interface}{$routerid}{$area} = {
1266             # graph => { N => link1, color => red, }
1267             # hashes => [ { link hash } ]
1268             # }
1269             ########################################################################
1270              
1271             # take link hash
1272             # detect inconsistencies and set colors
1273             sub check_link {
1274 2     2 0 4 my OSPF::LSDB::View6 $self = shift;
1275 2 50       4 my $lnkhash = $self->{lnkhash} or die "Uninitialized member";
1276 2         4 my %colors;
1277 2         6 while (my($intf,$iv) = each %$lnkhash) {
1278 4         10 while (my($rid, $rv) = each %$iv) {
1279 4         8 my $lid = "$intf\@$rid";
1280 4         9 while (my($area, $av) = each %$rv) {
1281 4         6 $colors{gray} = $area;
1282 4         6 %{$av->{colors}} = %colors;
  4         18  
1283             }
1284             }
1285             }
1286             }
1287              
1288             sub create_link {
1289 2     2 0 6 my OSPF::LSDB::View6 $self = shift;
1290 2         3 my $index = 0;
1291 2         2 my %lnkhash;
1292 2         3 foreach my $l (@{$self->{ospf}{database}{links}}) {
  2         10  
1293 4         7 my $intf = $l->{interface};
1294 4         6 my $rid = $l->{routerid};
1295 4         7 my $lid = "$intf\@$rid";
1296 4         5 my $area = $l->{area};
1297 4         6 my $linklocal = $l->{linklocal};
1298             my $prefixes = join("\\n",
1299 5         14 map { "$_->{prefixaddress}/$_->{prefixlength}" }
1300 4 50       7 @{$l->{prefixes} || []});
  4         10  
1301 4         9 my $elem = $lnkhash{$intf}{$rid}{$area};
1302 4 50       9 if (! $elem) {
1303 4         7 $lnkhash{$intf}{$rid}{$area} = $elem = {};
1304             $elem->{graph} = {
1305 4         23 N => "link$index",
1306             label => "$linklocal\\n$prefixes",
1307             shape => "hexagon",
1308             style => "solid",
1309             };
1310 4         8 $elem->{index} = $index++;
1311             }
1312 4         4 push @{$elem->{hashes}}, $l;
  4         10  
1313             }
1314 2         11 $self->{lnkhash} = \%lnkhash;
1315             }
1316              
1317             # take hash containing link nodes
1318             # return list of nodes
1319             sub link2nodes {
1320 2     2 0 4 my OSPF::LSDB::View6 $self = shift;
1321 2 50       5 my $lnkhash = $self->{lnkhash} or die "Uninitialized member";
1322 2         4 return $self->elements2graphs(map { values %$_ } map { values %$_ }
  4         7  
  4         7  
1323             values %$lnkhash);
1324             }
1325              
1326             # take link hash, network hash, router hash
1327             # return list of edges from network and router to link
1328             sub link2edges {
1329 2     2 0 4 my OSPF::LSDB::View6 $self = shift;
1330 2 50       5 my $lnkhash = $self->{lnkhash} or die "Uninitialized member";
1331 2 50       5 my $routehash = $self->{routehash} or die "Uninitialized member";
1332 2 50       5 my $transithash = $self->{transithash} or die "Uninitialized member";
1333 2 50       4 my $transitnets = $self->{transitnets} or die "Uninitialized member";
1334 2 50       5 my $nethash = $self->{nethash} or die "Uninitialized member";
1335 2         3 my @elements;
1336 2         3 my $index = 0;
1337 2         10 foreach my $intf (sort keys %$lnkhash) {
1338 4         6 my $iv = $lnkhash->{$intf};
1339 4         7 foreach my $rid (sort keys %$iv) {
1340 4         7 my $rv = $iv->{$rid};
1341 4         7 my $lid = "$intf\@$rid";
1342             my $rdst = $routehash->{$rid}{graph}{N}
1343 4 50       9 or die "No router graph $rid";
1344 4         7 foreach my $area (sort keys %$rv) {
1345 4         4 my $av = $rv->{$area};
1346 4         13 my $src = $av->{graph}{N};
1347 4         5 my %colors;
1348 4         12 $colors{gray} = $area;
1349 4         21 push @elements, {
1350             graph => {
1351             S => $src,
1352             D => $rdst,
1353             style => "bold",
1354             taillabel => $intf,
1355             },
1356             colors => \%colors,
1357             index => $index++,
1358             };
1359 4 50       18 my $tv = $transitnets->{$intf}{$rid}{$area}
1360             or next;
1361             # TODO check for duplicates in check_transit
1362 0         0 my($netaddr,$nv) = each %$tv;
1363 0         0 my($netrid,$num) = each %$nv;
1364             my $ndst = $nethash->{$netaddr}{$netrid}{$area}{graph}{N} ||
1365             $transithash->{$netaddr}{$netrid}{$area}{$rid}{graph}{N}
1366 0 0 0     0 or next;
1367 0         0 push @elements, {
1368             graph => {
1369             S => $src,
1370             D => $ndst,
1371             style => "solid",
1372             },
1373             colors => \%colors,
1374             index => $index++,
1375             };
1376             }
1377             }
1378             }
1379 2         6 return $self->elements2graphs(@elements);
1380             }
1381              
1382             ########################################################################
1383             # RFC 2740
1384             # LSA function code LS Type Description
1385             # ----------------------------------------------------
1386             # 9 0x2009 Intra-Area-Prefix-LSA
1387             # Referenced LS type 1 router-LSA
1388             ########################################################################
1389             # intrarouters => [
1390             # address => 'ipv4', # Link State ID
1391             # area => 'ipv4',
1392             # interface => 'ipv4', # Referenced Link State ID, 0
1393             # # 0
1394             # prefixes => [
1395             # prefixaddress => 'ipv6', # Address Prefix
1396             # prefixlength => 'int', # PrefixLength
1397             # ],
1398             # router => 'ipv4', # Referenced Advertising Router
1399             # # originating router's Router ID
1400             # routerid => 'ipv4', # Advertising Router
1401             # ],
1402             ########################################################################
1403             # $intraroutehash{$router}{$area} = {
1404             # graph => { N => intrarouter1, color => red, }
1405             # hashes => [ { intrarouter hash } ]
1406             # }
1407             ########################################################################
1408              
1409             # take intrarouter hash
1410             # detect inconsistencies and set colors
1411             sub check_intrarouter {
1412 4     4 0 6 my OSPF::LSDB::View6 $self = shift;
1413 4 50       9 my $intraroutehash = $self->{intraroutehash} or die "Uninitialized member";
1414 4         6 my %colors;
1415 4         12 while (my($rid, $rv) = each %$intraroutehash) {
1416 3         5 my $iid = "$rid";
1417 3         23 while (my($area, $av) = each %$rv) {
1418 4         8 $colors{gray} = $area;
1419 4         5 %{$av->{colors}} = %colors;
  4         19  
1420             }
1421             }
1422             }
1423              
1424             sub create_intrarouters {
1425 4     4 0 8 my OSPF::LSDB::View6 $self = shift;
1426 4         5 my $index = 0;
1427 4         5 my %intraroutehash;
1428 4         5 foreach my $i (@{$self->{ospf}{database}{intrarouters}}) {
  4         10  
1429 4         8 my $intf = $i->{interface};
1430 4         4 my $rid = $i->{router};
1431 4         5 my $area = $i->{area};
1432 4         7 my $elem = $intraroutehash{$rid}{$area};
1433 4 50       8 if (! $elem) {
1434 4         7 $intraroutehash{$rid}{$area} = $elem = {};
1435             $elem->{graph} = {
1436 4         29 N => "intrarouter$index",
1437             label => "prefixes",
1438             shape => "octagon",
1439             style => "solid",
1440             };
1441 4         8 $elem->{index} = $index++;
1442             }
1443 4         5 push @{$elem->{hashes}}, $i;
  4         9  
1444             $elem->{graph}{label} = join("\\n",
1445 5         18 map { "$_->{prefixaddress}/$_->{prefixlength}" }
1446 4 50       13 map { @{$_->{prefixes} || []} } @{$elem->{hashes}});
  4         11  
  4         12  
  4         9  
1447             }
1448 4         11 $self->{intraroutehash} = \%intraroutehash;
1449             }
1450              
1451             # take hash containing intrarouter nodes
1452             # return list of nodes
1453             sub intrarouter2nodes {
1454 4     4 0 5 my OSPF::LSDB::View6 $self = shift;
1455 4 50       9 my $intraroutehash = $self->{intraroutehash} or die "Uninitialized member";
1456 4         9 return $self->elements2graphs(map { values %$_ } values %$intraroutehash);
  3         8  
1457             }
1458              
1459             # take intrarouter hash, router hash
1460             # return list of edges from intrarouter to router
1461             sub intrarouter2edges {
1462 4     4 0 79 my OSPF::LSDB::View6 $self = shift;
1463 4 50       12 my $intraroutehash = $self->{intraroutehash} or die "Uninitialized member";
1464 4 50       8 my $routehash = $self->{routehash} or die "Uninitialized member";
1465 4         5 my @elements;
1466 4         6 my $index = 0;
1467 4         15 foreach my $rid (sort keys %$intraroutehash) {
1468 3         6 my $rv = $intraroutehash->{$rid};
1469 3         6 my $iid = "$rid";
1470 3         6 foreach my $area (sort keys %$rv) {
1471 4         9 my $av = $rv->{$area};
1472 4         8 my $src = $av->{graph}{N};
1473             my $dst = $routehash->{$rid}{graph}{N}
1474 4 50       9 or die "No router graph $rid";
1475 4         4 my %colors;
1476 4         5 $colors{gray} = $area;
1477 4         6 foreach my $i (@{$av->{hashes}}) {
  4         7  
1478 4         6 my $addr = $i->{address};
1479 4         29 push @elements, {
1480             graph => {
1481             S => $src,
1482             D => $dst,
1483             style => "bold",
1484             taillabel => $addr,
1485             },
1486             colors => { %colors },
1487             index => $index++,
1488             };
1489             }
1490             }
1491             }
1492 4         62 return $self->elements2graphs(@elements);
1493             }
1494              
1495             ########################################################################
1496             # RFC 2740
1497             # LSA function code LS Type Description
1498             # ----------------------------------------------------
1499             # 9 0x2009 Intra-Area-Prefix-LSA
1500             # Referenced LS type 2 network-LSA
1501             ########################################################################
1502             # intranetworks => [
1503             # address => 'ipv4', # Link State ID
1504             # area => 'ipv4',
1505             # interface => 'ipv4', # Referenced Link State ID
1506             # # Interface ID of Designated Router
1507             # prefixes => [
1508             # prefixaddress => 'ipv6', # Address Prefix
1509             # prefixlength => 'int', # PrefixLength
1510             # ],
1511             # router => 'ipv4', # Referenced Advertising Router
1512             # # Designated Router's Router ID
1513             # routerid => 'ipv4', # Advertising Router
1514             # ],
1515             ########################################################################
1516             # $intranethash{$interface}{$router}{$area} = {
1517             # graph => { N => intranetwork1, color => red, }
1518             # hashes => [ { intranetwork hash } ]
1519             # }
1520             ########################################################################
1521              
1522             # take intranetwork hash
1523             # detect inconsistencies and set colors
1524             sub check_intranetwork {
1525 4     4 0 6 my OSPF::LSDB::View6 $self = shift;
1526 4 50       10 my $intranethash = $self->{intranethash} or die "Uninitialized member";
1527 4         5 my %colors;
1528 4         11 while (my($intf,$iv) = each %$intranethash) {
1529 3         8 while (my($rid, $rv) = each %$iv) {
1530 3         6 my $iid = "$intf\@$rid";
1531 3         7 while (my($area, $av) = each %$rv) {
1532 3         4 $colors{gray} = $area;
1533 3         5 %{$av->{colors}} = %colors;
  3         14  
1534             }
1535             }
1536             }
1537             }
1538              
1539             sub create_intranetworks {
1540 4     4 0 10 my OSPF::LSDB::View6 $self = shift;
1541 4         7 my $index = 0;
1542 4         4 my %intranethash;
1543 4         5 foreach my $i (@{$self->{ospf}{database}{intranetworks}}) {
  4         15  
1544 4         8 my $intf = $i->{interface};
1545 4         5 my $rid = $i->{router};
1546 4         5 my $area = $i->{area};
1547 4         7 my $elem = $intranethash{$intf}{$rid}{$area};
1548 4 100       9 if (! $elem) {
1549 3         5 $intranethash{$intf}{$rid}{$area} = $elem = {};
1550             $elem->{graph} = {
1551 3         18 N => "intranetwork$index",
1552             label => "prefixes",
1553             shape => "octagon",
1554             style => "bold",
1555             };
1556 3         9 $elem->{index} = $index++;
1557             }
1558 4         5 push @{$elem->{hashes}}, $i;
  4         7  
1559             $elem->{graph}{label} = join("\\n",
1560 6         20 map { "$_->{prefixaddress}/$_->{prefixlength}" }
1561 4 50       6 map { @{$_->{prefixes} || []} } @{$elem->{hashes}});
  5         6  
  5         13  
  4         6  
1562             }
1563 4         13 $self->{intranethash} = \%intranethash;
1564             }
1565              
1566             # take hash containing intranetwork nodes
1567             # return list of nodes
1568             sub intranetwork2nodes {
1569 4     4 0 5 my OSPF::LSDB::View6 $self = shift;
1570 4 50       9 my $intranethash = $self->{intranethash} or die "Uninitialized member";
1571 4         7 return $self->elements2graphs(map { values %$_ } map { values %$_ }
  3         6  
  3         6  
1572             values %$intranethash);
1573             }
1574              
1575             # take intranetwork hash, network hash, router hash
1576             # return list of edges from intranetwork to network and router
1577             sub intranetwork2edges {
1578 4     4 0 6 my OSPF::LSDB::View6 $self = shift;
1579 4 50       13 my $intranethash = $self->{intranethash} or die "Uninitialized member";
1580 4 50       9 my $nethash = $self->{nethash} or die "Uninitialized member";
1581 4 50       7 my $routehash = $self->{routehash} or die "Uninitialized member";
1582 4         4 my @elements;
1583 4         5 my $index = 0;
1584 4         14 foreach my $intf (sort keys %$intranethash) {
1585 3         5 my $iv = $intranethash->{$intf};
1586 3         7 foreach my $rid (sort keys %$iv) {
1587 3         4 my $rv = $iv->{$rid};
1588 3         4 my $iid = "$intf\@$rid";
1589 3         7 foreach my $area (sort keys %$rv) {
1590 3         4 my $av = $rv->{$area};
1591 3         5 my $src = $av->{graph}{N};
1592             my $dst = $nethash->{$intf}{$rid}{$area}{graph}{N}
1593 3 50       7 or die "No network graph $intf $rid $area";
1594 3         9 my %colors;
1595 3         7 $colors{gray} = $area;
1596 3         3 foreach my $i (@{$av->{hashes}}) {
  3         9  
1597 4         12 my $addr = $i->{address};
1598 4         26 push @elements, {
1599             graph => {
1600             S => $src,
1601             D => $dst,
1602             style => "bold",
1603             taillabel => $addr,
1604             },
1605             colors => { %colors },
1606             index => $index++,
1607             };
1608             }
1609             }
1610             }
1611             }
1612 4         9 return $self->elements2graphs(@elements);
1613             }
1614              
1615             # return legend routers as dot graph
1616             sub legend_router {
1617 1     1 0 3 my $class = shift;
1618 1         3 my $index = 0;
1619 1         7 my @nodes = (
1620             {
1621             label => 'ospf\nrouter',
1622             }, {
1623             label => 'current\nlocation',
1624             peripheries => 2,
1625             }, {
1626             label => 'area border\nrouter',
1627             style => 'bold',
1628             }, {
1629             label => 'summary AS\nboundary router',
1630             style => 'dashed',
1631             },
1632             );
1633 1         3 foreach (@nodes) {
1634 4         17 $_->{N} = 'router'. $index++;
1635 4   50     15 $_->{shape} ||= 'box';
1636 4   100     10 $_->{style} ||= 'solid';
1637             }
1638              
1639 1         2 my $dot = "";
1640 1         6 $dot .= $class->graph_nodes(@nodes);
1641 1         5 $dot .= "\t{ rank=same;";
1642 1         3 $dot .= join("", map { " $_->{N};" } @nodes);
  4         11  
1643 1         2 $dot .= " }\n";
1644 1         5 return $dot;
1645             }
1646              
1647             # return legend networks as dot graph
1648             sub legend_network {
1649 1     1 0 2 my $class = shift;
1650 1         2 my $index = 0;
1651 1         7 my @nodes = (
1652             {
1653             label => 'transit\nnetwork',
1654             style => 'bold',
1655             }, {
1656             label => 'summary\nnetwork',
1657             style => 'dashed',
1658             }, {
1659             color => 'gray35',
1660             label => 'AS external\nnetwork',
1661             shape => 'egg',
1662             }, {
1663             label => 'link\nprefix',
1664             shape => 'hexagon',
1665             }, {
1666             label => 'intra-area\nprefix',
1667             shape => 'octagon',
1668             },
1669             );
1670 1         2 foreach (@nodes) {
1671 5         10 $_->{N} = 'network'. $index++;
1672 5   100     12 $_->{shape} ||= 'ellipse';
1673 5   100     11 $_->{style} ||= 'solid';
1674             }
1675              
1676 1         2 my $dot = "";
1677 1         12 $dot .= $class->graph_nodes(@nodes);
1678 1         3 $dot .= "\t{ rank=same;";
1679 1         2 $dot .= join("", map { " $_->{N};" } @nodes);
  5         11  
1680 1         2 $dot .= " }\n";
1681 1         6 return $dot;
1682             }
1683              
1684             # return legend router network edges as dot graph
1685             sub legend_edge {
1686 1     1 0 2 my $class = shift;
1687 1         9 my @networknodes = (
1688             {
1689             label => 'network',
1690             }, {
1691             label => 'transit\nnetwork',
1692             style => 'bold',
1693             }, {
1694             color => 'gray35',
1695             label => 'ASE type 1\nnetwork',
1696             shape => 'egg',
1697             }, {
1698             color => 'gray35',
1699             label => 'ASE type 2\nnetwork',
1700             shape => 'egg',
1701             }, {
1702             label => 'link\nprefix',
1703             shape => 'hexagon',
1704             }, {
1705             label => 'intra-area\nrouter prefix',
1706             shape => 'octagon',
1707             },
1708             );
1709 1         3 foreach (@networknodes) {
1710 6   100     14 $_->{shape} ||= 'ellipse';
1711 6   100     13 $_->{style} ||= 'solid';
1712             }
1713              
1714 1         5 my @routernodes = (
1715             {
1716             label => 'router',
1717             }, {
1718             label => 'designated\nrouter',
1719             }, {
1720             label => 'AS boundary\nrouter',
1721             }, {
1722             label => 'AS boundary\nrouter',
1723             }, {
1724             label => 'router',
1725             },
1726             );
1727 1         3 foreach (@routernodes) {
1728 5   50     14 $_->{shape} ||= 'box';
1729 5   50     11 $_->{style} ||= 'solid';
1730             }
1731              
1732 1         2 my $index = 0;
1733 1         9 my @edges = (
1734             {
1735             headlabel => 'Interface',
1736             style => 'solid',
1737             taillabel => 'cost',
1738             }, {
1739             style => 'bold',
1740             }, {
1741             color => 'gray35',
1742             headlabel => 'cost',
1743             style => 'solid',
1744             taillabel => 'LS-ID'
1745             }, {
1746             color => 'gray35',
1747             headlabel => 'cost',
1748             style => 'dashed',
1749             taillabel => 'LS-ID'
1750             }, {
1751             style => 'bold',
1752             taillabel => 'Interface'
1753             }, {
1754             style => 'bold',
1755             taillabel => 'LS-ID'
1756             },
1757             );
1758 1         4 for(my $i=0; $i<@edges; $i++) {
1759 6         10 $networknodes[$i]{N} = 'edgenetwork'. $index;
1760 6         19 $routernodes [$i]{N} = 'edgerouter'. $index;
1761 6         8 $edges [$i]{S} = 'edgenetwork'. $index;
1762 6         11 $edges [$i]{D} = 'edgerouter'. $index;
1763 6         11 $index++;
1764             }
1765             # swap arrow for cost IF explanation
1766 1         3 ($edges[0]{D}, $edges[0]{S}) = ($edges[0]{S}, $edges[0]{D});
1767             # link and intra area prefix have same router destination
1768 1         3 $edges[-1]{D} = $edges[-2]{D};
1769 1         2 pop @routernodes;
1770              
1771 1         3 my $dot = "";
1772 1         3 $dot .= $class->graph_nodes(@networknodes);
1773 1         5 $dot .= $class->graph_nodes(@routernodes);
1774 1         6 $dot .= $class->graph_edges(@edges);
1775 1         3 $dot .= "\t{ rank=same;";
1776 1         3 $dot .= join("", map { " $_->{S};" } @edges);
  6         12  
1777 1         2 $dot .= " }\n";
1778 1         11 return $dot;
1779             }
1780              
1781             # return legend router link to router or network as dot graph
1782             sub legend_link {
1783 1     1 0 2 my $class = shift;
1784 1         6 my @routernodes = (
1785             {}, {}, {
1786             label => 'designated\nrouter',
1787             }, {}, {
1788             label => 'link\nprefix',
1789             shape => 'hexagon',
1790             }, {
1791             label => 'intra-area\nnetwork prefix',
1792             shape => 'octagon',
1793             },
1794             );
1795 1         3 foreach (@routernodes) {
1796 6   100     15 $_->{label} ||= 'router';
1797 6   100     15 $_->{shape} ||= 'box';
1798 6   50     14 $_->{style} ||= 'solid';
1799             }
1800              
1801 1         27 my @dstnodes = (
1802             {}, {
1803             label => 'transit\nnetwork',
1804             style => 'bold',
1805             shape => 'ellipse',
1806             }, {
1807             label => 'transit\nnetwork',
1808             style => 'bold',
1809             shape => 'ellipse',
1810             }, {}, {
1811             label => 'transit\nnetwork',
1812             style => 'bold',
1813             shape => 'ellipse',
1814             },
1815             );
1816 1         3 foreach (@dstnodes) {
1817             $_->{label} ||= 'router',
1818 5   100     17 $_->{shape} ||= 'box';
      100        
1819 5   100     10 $_->{style} ||= 'solid';
1820             }
1821              
1822 1         7 my $index = 0;
1823 1         6 my @edges = (
1824             {
1825             label => 'point-to-point\nlink',
1826             }, {
1827             label => 'link to\ntransit network',
1828             }, {
1829             label => 'link to\ntransit network',
1830             style => 'bold',
1831             }, {
1832             label => 'virtual\nlink',
1833             style => 'dotted',
1834             }, {
1835             style => 'solid',
1836             }, {
1837             style => 'bold',
1838             taillabel => 'LS-ID',
1839             },
1840             );
1841 1         2 foreach (@edges) {
1842 6   100     12 $_->{style} ||= 'solid';
1843             }
1844 1         7 for(my $i=0; $i<@edges; $i++) {
1845 6         12 $routernodes[$i]{N} = 'linkrouter'. $index;
1846 6         9 $dstnodes [$i]{N} = 'linkdst'. $index;
1847 6         8 $edges [$i]{S} = 'linkrouter'. $index;
1848 6         9 $edges [$i]{D} = 'linkdst'. $index;
1849 6         10 $index++;
1850             }
1851             # link and intra area prefix have same network destination
1852 1         5 $edges[-1]{D} = $edges[-2]{D};
1853 1         1 pop @dstnodes;
1854              
1855 1         2 my $dot = "";
1856 1         3 $dot .= $class->graph_nodes(@routernodes);
1857 1         14 $dot .= $class->graph_nodes(@dstnodes);
1858 1         5 $dot .= $class->graph_edges(@edges);
1859 1         4 $dot .= "\t{ rank=same;";
1860 1         4 $dot .= join("", map { " $_->{S};" } @edges);
  6         12  
1861 1         14 $dot .= " }\n";
1862 1         11 return $dot;
1863             }
1864              
1865             # return legend summary network and router edges as dot graph
1866             sub legend_summary {
1867 1     1 0 2 my $class = shift;
1868 1         7 my @networknodes = (
1869             {
1870             label => 'summary\nnetwork',
1871             style => 'dashed',
1872             }, {
1873             label => 'summary AS\nboundary router',
1874             shape => 'box',
1875             style => 'dashed',
1876             }, {
1877             label => 'router and summary \nAS boundary router',
1878             shape => 'box',
1879             }, {
1880             color => 'gray35',
1881             label => 'ASE\nnetwork',
1882             shape => 'egg',
1883             },
1884             );
1885 1         3 foreach (@networknodes) {
1886 4   100     8 $_->{shape} ||= 'ellipse';
1887 4   100     11 $_->{style} ||= 'solid';
1888             }
1889              
1890 1         4 my @routernodes = (
1891             {}, {}, {
1892             color => 'black',
1893             }, {
1894             color => 'gray35',
1895             label => 'summary AS\nboundary router',
1896             style => 'dashed',
1897             },
1898             );
1899 1         2 foreach (@routernodes) {
1900 4   100     13 $_->{label} ||= 'area border\nrouter';
1901 4   50     13 $_->{shape} ||= 'box';
1902 4   100     9 $_->{style} ||= 'bold';
1903             }
1904              
1905 1         4 my $index = 0;
1906 1         7 my @edges = (
1907             {
1908             headlabel => 'cost',
1909             style => 'dashed',
1910             taillabel => 'LS-ID'
1911             }, {
1912             headlabel => 'cost',
1913             style => 'dashed',
1914             taillabel => 'LS-ID'
1915             }, {
1916             color => 'gray75',
1917             headlabel => 'cost',
1918             style => 'dashed',
1919             taillabel => 'LS-ID'
1920             }, {
1921             color => 'gray35',
1922             headlabel => 'cost',
1923             style => 'solid',
1924             taillabel => 'LS-ID'
1925             },
1926             );
1927 1         4 for(my $i=0; $i<@edges; $i++) {
1928 4         8 $networknodes[$i]{N} = 'summarynetwork'. $index;
1929 4         7 $routernodes [$i]{N} = 'summaryrouter'. $index;
1930 4         6 $edges [$i]{S} = 'summarynetwork'. $index;
1931 4         7 $edges [$i]{D} = 'summaryrouter'. $index;
1932 4         8 $index++;
1933             }
1934              
1935 1         2 my $dot = "";
1936 1         3 $dot .= $class->graph_nodes(@networknodes);
1937 1         3 $dot .= $class->graph_nodes(@routernodes);
1938 1         5 $dot .= $class->graph_edges(@edges);
1939 1         3 $dot .= "\t{ rank=same;";
1940 1         2 $dot .= join("", map { " $_->{S};" } @edges);
  4         10  
1941 1         2 $dot .= " }\n";
1942 1         10 return $dot;
1943             }
1944              
1945             =pod
1946              
1947             =head1 SEE ALSO
1948              
1949             L,
1950             L
1951              
1952             L,
1953             L
1954              
1955             RFC 5340 - OSPF for IPv6 - July 2008
1956              
1957             =head1 AUTHORS
1958              
1959             Alexander Bluhm
1960              
1961             =head1 BUGS
1962              
1963             IPv6 support has not been finished yet.
1964             Especially there are much less checks than in IPv4.
1965              
1966             =cut
1967              
1968             1;