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   2402 use strict;
  11         22  
  11         302  
18 11     11   51 use warnings;
  11         20  
  11         424  
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   63 use base 'OSPF::LSDB::View';
  11         20  
  11         2250  
51 11         79 use fields qw (
52             sumlsids
53             boundlsids
54             externlsids
55             lnkhash
56             intraroutehash
57             intranethash
58 11     11   69 );
  11         14  
59              
60             sub new {
61 71     71 1 69849 my OSPF::LSDB::View6 $self = OSPF::LSDB::new(@_);
62 71 50       285 die "$_[0] does not support IPv4" unless $self->ipv6();
63 71         149 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 96 my OSPF::LSDB::View6 $self = shift;
99 71 50       210 my $routehash = $self->{routehash} or die "Uninitialized member";
100 71         256 while (my($rid,$rv) = each %$routehash) {
101 146         150 my %colors;
102 146         188 my @areas = sort keys %{$rv->{areas}};
  146         391  
103 146 100       324 if (@areas > 1) {
104 26         65 $colors{black} = \@areas;
105 26 100       40 if (my @badareas = map { $_->{area} || () }
  3 100       25  
106 53         145 grep { ! $_->{bits}{B} } @{$rv->{hashes}}) {
  26         49  
107             $self->error($colors{orange} =
108 1         8 "Router $rid in multiple areas is not border router ".
109             "in areas @badareas.");
110             }
111             } else {
112 120         308 $colors{gray} = $areas[0];
113             }
114 146 100       249 if ($rv->{missing}) {
115 12         78 $self->error($colors{red} = "Router $rid missing.");
116             } else {
117 134         143 while (my($area,$av) = each %{$rv->{areas}}) {
  295         561  
118             # TODO check wether bits are equal
119 161         357 while (my($lsid,$num) = each %$av) {
120 161 100       401 if ($num > 1) {
121             $self->error($colors{magenta} =
122 1         28 "Router $rid has multiple link state IDs $lsid ".
123             "in area $area.");
124             }
125             }
126             }
127             }
128 146         436 $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 125 my OSPF::LSDB::View6 $self = shift;
137 71         155 my($index) = @_;
138 71         83 my %rid2areas;
139 71 50       193 my $nethash = $self->{nethash} or die "Uninitialized member";
140 54         65 my @hashes = map { @{$_->{hashes}} } map { values %$_ }
  54         122  
  53         102  
141 71         204 map { values %$_ } values %$nethash;
  58         144  
142 71         200 foreach my $n (@hashes) {
143 56         104 my $area = $n->{area};
144 56         143 $rid2areas{$n->{routerid}}{$area} = 1;
145 56         67 foreach (@{$n->{attachments}}) {
  56         113  
146 109         190 $rid2areas{$_->{routerid}}{$area} = 1;
147             }
148             }
149 71         130 my $intraroutehash = $self->{intraroutehash};
150 71         173 @hashes = map { @{$_->{hashes}} } map { values %$_ }
  4         5  
  4         7  
  3         8  
151             values %$intraroutehash;
152 71         122 foreach my $ir (@hashes) {
153 4         6 my $area = $ir->{area};
154 4         8 $rid2areas{$ir->{router}}{$area} = 1;
155             }
156 71         107 my $lnkhash = $self->{lnkhash};
157 4         5 @hashes = map { @{$_->{hashes}} } map { values %$_ }
  4         7  
  4         8  
158 71         188 map { values %$_ } values %$lnkhash;
  4         8  
159 71         109 foreach my $l (@hashes) {
160 4         6 my $area = $l->{area};
161 4         9 $rid2areas{$l->{routerid}}{$area} = 1;
162             }
163 71         249 $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 163 my OSPF::LSDB::View6 $self = shift;
206 142         199 my($type) = @_;
207 142 100       256 my $name = $type eq "pointtopoint" ? "Point-to-point" : "Virtual";
208 142 100       208 my $style = $type eq "pointtopoint" ? "solid" : "dotted";
209 142 50       255 my $routehash = $self->{routehash} or die "Uninitialized member";
210 142 50       351 my $linkhash = $self->{$type."hash"} or die "Uninitialized member";
211 142         197 my $ifaddrs = $self->{ifaddrs};
212 142         156 my @elements;
213 142         135 my $index = 0;
214 142         295 foreach my $dstrid (sort keys %$linkhash) {
215 22         25 my $dv = $linkhash->{$dstrid};
216 22         42 foreach my $area (sort keys %$dv) {
217 22         24 my $ev = $dv->{$area};
218 22         41 foreach my $rid (sort keys %$ev) {
219 22         23 my $rv = $ev->{$rid};
220 22         55 my %colors = (gray => $area);
221 22         36 my $src = $routehash->{$rid}{graph}{N};
222 22         33 my $dst = $routehash->{$dstrid}{graph}{N};
223 22         22 my @hashes = @{$rv->{hashes}};
  22         33  
224 22 100 100     77 if ($type ne "pointtopoint" && @hashes > 1) {
225             $self->error($colors{yellow} =
226 2         13 "$name link at router $rid to router $dstrid ".
227             "has multiple entries in area $area.");
228             }
229 22 100 100     136 if (! $routehash->{$dstrid}{areas}{$area}) {
    100          
230             $self->error($colors{orange} =
231 4         19 "$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         12 "$name link at router $rid to router $dstrid ".
238             "not symmetric in area $area.");
239             }
240 22         40 foreach my $link (@hashes) {
241 25         33 my $intf = $link->{interface};
242 25         30 delete $colors{green};
243 25 100 66     71 if ($type eq "pointtopoint" and $ifaddrs->{$intf} &&
      100        
244             $ifaddrs->{$intf}{$rid} > 1) {
245             $self->error($colors{green} =
246 1         8 "$name link at router $rid to router $dstrid ".
247             "interface address $intf not unique.");
248             }
249 25         39 my $metric = $link->{metric};
250 25         183 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         279 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 115 my OSPF::LSDB::View6 $self = shift;
292 71         110 my($transitcluster) = @_;
293 71 50       207 my $nethash = $self->{nethash} or die "Uninitialized member";
294 71 50       147 my $transithash = $self->{transithash} or die "Uninitialized member";
295 71         207 foreach my $addr (sort keys %$transithash) {
296 56         77 my $av = $transithash->{$addr};
297             # TODO check if the there is more than one designated neigbor
298 56         122 foreach my $netrid (sort keys %$av) {
299 56         74 my $nv = $av->{$netrid};
300 56         80 my %colors;
301 56 100 100     166 if (! $nethash->{$addr}{$netrid} &&
302             keys %$nv > 1) {
303             $self->error($colors{orange} =
304 2         12 "Transit network $addr\@$netrid missing in multiple areas.");
305             }
306 56         143 foreach my $area (sort keys %$nv) {
307 60         85 my $ev = $nv->{$area};
308 60         97 $colors{gray} = $area;
309 60         74 delete $colors{blue};
310 60 100 100     139 if (! $nethash->{$addr}{$netrid} && keys %$ev > 1) {
311             $self->error($colors{blue} =
312 1         20 "Transit network $addr\@$netrid missing in area $area ".
313             "at multiple routers.");
314             }
315 60         160 foreach my $rid (sort keys %$ev) {
316 104         108 my $rv = $ev->{$rid};
317 104 100       365 next unless $rv->{graph};
318 9         19 delete @colors{qw(yellow red)};
319 9 100 100     35 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         12 "Transit network $addr\@$netrid network missing.");
326             }
327 9         18 %{$rv->{colors}} = %colors;
  9         22  
328 9         13 push @{$transitcluster->{$addr}}, $rv->{graph};
  9         28  
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 120 my OSPF::LSDB::View6 $self = shift;
339 106         204 my($transithash, $transitnets, $index, $rid, $area, $link) = @_;
340 106 50       184 my $nethash = $self->{nethash} or die "Uninitialized member";
341 106         150 my $addr = $link->{address};
342 106         123 my $netrid = $link->{routerid};
343 106         130 my $intf = $link->{interface};
344 106         323 $transitnets->{$intf}{$rid}{$area}{$addr}{$netrid}++;
345 106         212 my $elem = $transithash->{$addr}{$netrid}{$area}{$rid};
346 106 100       161 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     394 if (! $nethash->{$addr}{$netrid} ||
350             ! $nethash->{$addr}{$netrid}{$area}) {
351             $elem->{graph} = {
352 9         58 N => "transitnet$$index",
353             label => "$addr\\n$netrid",
354             shape => "ellipse",
355             style => "dotted",
356             };
357 9         21 $elem->{index} = $$index++;
358             }
359             }
360 106         114 push @{$elem->{hashes}}, $link;
  106         295  
361             }
362              
363             # take hash containing transit network nodes
364             # return list of nodes
365             sub transit2nodes {
366 71     71 0 92 my OSPF::LSDB::View6 $self = shift;
367 71 50       177 my $transithash = $self->{transithash} or die "Uninitialized member";
368 60         143 return $self->elements2graphs(map { values %$_ } map { values %$_ }
  56         101  
369 71         162 map { values %$_ } values %$transithash);
  56         93  
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 101 my OSPF::LSDB::View6 $self = shift;
376 71 50       157 my $nethash = $self->{nethash} or die "Uninitialized member";
377 71 50       151 my $routehash = $self->{routehash} or die "Uninitialized member";
378 71 50       163 my $transithash = $self->{transithash} or die "Uninitialized member";
379 71         95 my $ifaddrs = $self->{ifaddrs};
380 71         90 my @elements;
381 71         86 my $index = 0;
382 71         154 foreach my $addr (sort keys %$transithash) {
383 56         73 my $av = $transithash->{$addr};
384 56         98 foreach my $netrid (sort keys %$av) {
385 56         120 my $nv = $av->{$netrid};
386 56         108 my $nid = "$addr\@$netrid";
387 56         96 foreach my $area (sort keys %$nv) {
388 60         74 my $ev = $nv->{$area};
389 60         131 foreach my $rid (sort keys %$ev) {
390 104         125 my $rv = $ev->{$rid};
391 104         172 my %colors = (gray => $area);
392 104         148 my $src = $routehash->{$rid}{graph}{N};
393 104 100       101 if (@{$rv->{hashes}} > 1) {
  104         175  
394             $self->error($colors{yellow} =
395 2         12 "Transit network $nid at router $rid ".
396             "has multiple entries in area $area.");
397             }
398 104         132 foreach my $link (@{$rv->{hashes}}) {
  104         151  
399 106         132 my $intf = $link->{interface};
400 106         124 delete $colors{green};
401 106 100 66     311 if ($ifaddrs->{$intf} && $ifaddrs->{$intf}{$rid} > 1) {
402             $self->error($colors{green} =
403 3         16 "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     250 my $style = $netrid eq $rid && $addr eq $intf ?
409             "bold" : "solid";
410 106         105 delete $colors{magenta};
411 106         97 delete $colors{brown};
412 106         109 delete $colors{tan};
413 106 100       168 if ($rv->{graph}) {
414 10         18 my $dst = $rv->{graph}{N};
415 10         60 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         33 next;
427             }
428 96         129 my $nv = $nethash->{$addr}{$netrid};
429 96         219 delete $colors{magenta};
430 96 50       138 my $ev = $nv->{$area}
431             or next;
432 96         96 delete $colors{brown};
433 96         94 delete $colors{tan};
434 96 100       142 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         673 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         181 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 102 my OSPF::LSDB::View6 $self = shift;
487 71         195 my($netcluster) = @_;
488 71 50       262 my $nethash = $self->{nethash} or die "Uninitialized member";
489 71 50       170 my $nets = $self->{nets} or die "Uninitialized member";
490 71         90 my %colors;
491 71         267 foreach my $addr (sort keys %$nethash) {
492 58         91 my $av = $nethash->{$addr};
493 58         110 foreach my $rid (sort keys %$av) {
494 53         80 my $rv = $av->{$rid};
495 53         108 my $nid = "$addr\@$rid";
496 53         76 delete $colors{green};
497 53 100       114 if ($nets->{$addr}{$rid} > 1) {
498             $self->error($colors{green} =
499 3         38 "Network $nid not unique at router $rid.");
500             }
501 53         75 delete $colors{orange};
502 53 100       130 if (keys %$rv > 1) {
503             $self->error($colors{orange} =
504 1         7 "Network $nid at router $rid in multiple areas.");
505             }
506 53         121 foreach my $area (sort keys %$rv) {
507 54         74 my $ev = $rv->{$area};
508 54 100       95 if ($ev->{missing}) {
509 1         6 $self->error($colors{red} = "Network $nid missing ".
510             "in area $area.");
511             } else {
512 53         81 $colors{gray} = $area;
513 53         66 delete $colors{yellow};
514 53 100       54 if (@{$ev->{hashes}} > 1) {
  53         104  
515             $self->error($colors{yellow} =
516 2         11 "Network $nid at router $rid ".
517             "has multiple entries in area $area.");
518             }
519 53         67 delete $colors{brown};
520 53         64 my @attrids = keys %{$ev->{attachrouters}};
  53         152  
521 53 100       148 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       137 if (@attrids == 1) {
527             $self->error($colors{brown} =
528 1         9 "Network $nid at router $rid attached only ".
529             "to router @attrids in area $area.");
530             }
531             }
532 54         87 %{$ev->{colors}} = %colors;
  54         122  
533             # TODO move netcluster to prefix lsa
534 54         76 push @{$netcluster->{"$addr\@$rid"}}, $ev->{graph};
  54         240  
535             }
536             }
537             }
538             }
539              
540             # take network structure, net cluster hash
541             # return network hash
542             sub create_network {
543 71     71 0 113 my OSPF::LSDB::View6 $self = shift;
544 71         125 my($index) = @_;
545 71         159 my %nethash;
546             my %nets;
547 71         0 my %netareas;
548 71         92 foreach my $n (@{$self->{ospf}{database}{networks}}) {
  71         221  
549 55         127 my $addr = $n->{address};
550 55         84 my $rid = $n->{routerid};
551 55         104 my $nid = "$addr\@$rid";
552 55         166 $nets{$addr}{$rid}++;
553 55         76 my $area = $n->{area};
554 55         119 $netareas{$addr}{$rid}{$area}++;
555 55         116 my $elem = $nethash{$addr}{$rid}{$area};
556 55 100       114 if (! $elem) {
557 53         128 $nethash{$addr}{$rid}{$area} = $elem = {};
558             $elem->{graph} = {
559 53         333 N => "network$$index",
560             label => "$addr\\n$rid",
561             shape => "ellipse",
562             style => "bold",
563             };
564 53         144 $elem->{index} = $$index++;
565             }
566 55         64 push @{$elem->{hashes}}, $n;
  55         125  
567 55         63 foreach my $att (@{$n->{attachments}}) {
  55         113  
568 109         304 $elem->{attachrouters}{$att->{routerid}} = 1;
569             }
570             }
571 71         166 $self->{nethash} = \%nethash;
572 71         109 $self->{nets} = \%nets;
573             # TODO netareas should handle prefixes
574 71         187 $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 112 my OSPF::LSDB::View6 $self = shift;
582 71         140 my($index) = @_;
583 71         148 my $intranethash = $self->{intranethash};
584 71 50       164 my $nethash = $self->{nethash} or die "Uninitialized member";
585 71 50       222 my $nets = $self->{nets} or die "Uninitialized member";
586 71 50       285 my $netareas = $self->{netareas} or die "Uninitialized member";
587 71         267 foreach my $addr (sort keys %$intranethash) {
588 3         6 my $av = $intranethash->{$addr};
589 3         9 foreach my $rid (sort keys %$av) {
590 3         6 my $rv = $av->{$rid};
591 3         8 foreach my $area (sort keys %$rv) {
592 3         4603 my $ev = $rv->{$area};
593 3         20 my $elem = $nethash->{$addr}{$rid}{$area};
594 3 100       16 if (! $elem) {
595 1         3 $nets->{$addr}{$rid}++;
596 1         3 $netareas->{$addr}{$rid}{$area}++;
597 1         3 $nethash->{$addr}{$rid}{$area} = $elem = {};
598             $elem->{graph} = {
599 1         14 N => "network$$index",
600             label => "$addr\\n$rid",
601             shape => "ellipse",
602             style => "dotted",
603             };
604 1         5 $elem->{index} = $$index++;
605 1         2 push @{$elem->{hashes}}, {
  1         4  
606             area => $area,
607             routerid => $rid,
608             };
609 1         6 $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 115 my OSPF::LSDB::View6 $self = shift;
620 71 50       153 my $nethash = $self->{nethash} or die "Uninitialized member";
621 71         154 return $self->elements2graphs(map { values %$_ } map { values %$_ }
  53         119  
  58         96  
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 110 my OSPF::LSDB::View6 $self = shift;
629 71 50       191 my $nethash = $self->{nethash} or die "Uninitialized member";
630 71 50       180 my $routehash = $self->{routehash} or die "Uninitialized member";
631 71 50       166 my $transithash = $self->{transithash} or die "Uninitialized member";
632 71         66 my @elements;
633 71         86 my $index = 0;
634 71         149 foreach my $addr (sort keys %$nethash) {
635 58         72 my $av = $nethash->{$addr};
636 58         102 foreach my $rid (sort keys %$av) {
637 53         67 my $rv = $av->{$rid};
638 53         106 my $nid = "$addr\@$rid";
639 53         114 foreach my $area (sort keys %$rv) {
640 54         62 my $ev = $rv->{$area};
641 54         61 my $src = $ev->{graph}{N};
642 54         62 foreach my $net (@{$ev->{hashes}}) {
  54         98  
643 56         59 my %attcolors;
644 56         60 foreach (@{$net->{attachments}}) {
  56         120  
645 109         157 my $arid = $_->{routerid};
646 109 100       154 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         170 $attcolors{$arid}{gray} = $area;
653 107 100 66     353 if ($routehash->{$arid}{areas} &&
654             ! $routehash->{$arid}{areas}{$area}) {
655             $self->error($attcolors{$arid}{orange} =
656 4         28 "Network $nid and router $arid ".
657             "not in same area $area.");
658 4         8 next;
659             }
660 103         175 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     360 if ($arid eq $rid && $tv && ! grep { $addr eq
      100        
668 47         169 $_->{interface} } @{$tv->{hashes}}) {
  46         80  
669             $self->error($attcolors{$arid}{tan} =
670 5         25 "Network $nid at router $arid in area $area ".
671             "is designated but transit link is not.");
672 5         8 next;
673             }
674             }
675 56         75 foreach (@{$net->{attachments}}) {
  56         85  
676 109         164 my $arid = $_->{routerid};
677             my $dst = $routehash->{$arid}{graph}{N}
678 109 50       176 or die "No router graph $arid";
679 109         111 my $style = "solid";
680 109 100       150 if ($arid eq $rid) {
681             # router is designated router
682 54         63 $style = "bold";
683             }
684             push @elements, {
685             graph => {
686             S => $src,
687             D => $dst,
688             style => $style,
689             },
690 109         258 colors => { %{$attcolors{$arid}} },
  109         388  
691             index => $index++,
692             };
693             }
694 56 100       201 if (! $attcolors{$rid}) {
695             my $dst = $routehash->{$rid}{graph}{N}
696 4 50       12 or die "No router graph $rid";
697 4         9 $attcolors{$rid}{gray} = $area;
698             $self->error($attcolors{$rid}{red} =
699 4         22 "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         14 colors => { %{$attcolors{$rid}} },
  4         24  
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 72 my OSPF::LSDB::View6 $self = shift;
744 52         86 my($netcluster) = @_;
745 52 50       112 my $netareas = $self->{netareas} or die "Uninitialized member";
746 52 50       165 my $sumhash = $self->{sumhash} or die "Uninitialized member";
747 52         133 foreach my $paddr (sort keys %$sumhash) {
748 9         14 my $av = $sumhash->{$paddr};
749 9         48 foreach my $plen (sort keys %$av) {
750 9         12 my $lv = $av->{$plen};
751 9         11 my %colors;
752 9         20 my $nid = "$paddr/$plen";
753 9         11 my @areas = sort keys %{$lv->{arearids}};
  9         25  
754 9 100       32 if (@areas > 1) {
755 5         10 $colors{black} = \@areas;
756             } else {
757 4         9 $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         13 $lv->{colors} = \%colors;
773 9         108 push @{$netcluster->{"$paddr/$plen"}}, $lv->{graph};
  9         49  
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 82 my OSPF::LSDB::View6 $self = shift;
782 52         66 my $index = 0;
783 52         120 my %sumhash;
784             my %sums;
785 52         0 my %sumlsids;
786 52         67 foreach my $s (@{$self->{ospf}{database}{summarys}}) {
  52         147  
787 17         28 my $paddr = $s->{prefixaddress};
788 17         27 my $plen = $s->{prefixlength};
789 17         30 my $nid = "$paddr/$plen";
790 17         21 my $rid = $s->{routerid};
791 17         22 my $addr = $s->{address};
792 17         23 my $area = $s->{area};
793 17         138 $sumlsids{$area}{$rid}{$addr}++;
794 17         41 my $elem = $sumhash{$paddr}{$plen};
795 17 100       33 if (! $elem) {
796 9         23 $sumhash{$paddr}{$plen} = $elem = {};
797             $elem->{graph} = {
798 9         47 N => "summary$index",
799             label => "$paddr/$plen",
800             shape => "ellipse",
801             style => "dashed",
802             };
803 9         19 $elem->{index} = $index++;
804             }
805 17         22 push @{$elem->{hashes}}, $s;
  17         127  
806 17         78 $elem->{arearids}{$area}{$rid}++;
807             }
808 52         82 $self->{sumhash} = \%sumhash;
809 52         122 $self->{sums} = \%sums;
810 52         128 $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 73 my OSPF::LSDB::View6 $self = shift;
817 52 50       127 my $routehash = $self->{routehash} or die "Uninitialized member";
818 52 50       147 my $sumhash = $self->{sumhash} or die "Uninitialized member";
819 52 50       122 my $sumlsids = $self->{sumlsids} or die "Uninitialized member";
820 52         52 my @elements;
821 52         63 my $index = 0;
822 52         127 foreach my $paddr (sort keys %$sumhash) {
823 9         14 my $av = $sumhash->{$paddr};
824 9         17 foreach my $plen (sort keys %$av) {
825 9         14 my $lv = $av->{$plen};
826 9         16 my $nid = "$paddr/$plen";
827 9   33     38 my $src = $lv->{graph} && $lv->{graph}{N};
828 9         13 foreach my $s (@{$lv->{hashes}}) {
  9         16  
829 17         24 my $rid = $s->{routerid};
830             my $dst = $routehash->{$rid}{graph}{N}
831 17 50       32 or die "No router graph $rid";
832 17         24 my $addr = $s->{address};
833 17         19 my $area = $s->{area};
834 17         32 my %colors = (gray => $area);
835 17 100       29 if (! $routehash->{$rid}{areas}{$area}) {
836             $self->error($colors{orange} =
837 1         8 "Summary network $nid and router $rid ".
838             "not in same area $area.");
839             }
840 17 100       36 if ($lv->{arearids}{$area}{$rid} > 1) {
841             $self->error($colors{yellow} =
842 4         18 "Summary network $nid at router $rid ".
843             "has multiple entries in area $area.");
844             }
845 17 100       36 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         23 my $metric = $s->{metric};
851             $s->{graph} = {
852 17         63 S => $src,
853             D => $dst,
854             headlabel => $metric,
855             style => "dashed",
856             taillabel => $addr,
857             };
858 17         45 $s->{colors} = \%colors;
859 17         24 $s->{index} = $index++;
860             # in case of aggregation src is undef
861 17 50       45 push @elements, $s if $src;
862             }
863             }
864             }
865 52         104 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 82 my OSPF::LSDB::View6 $self = shift;
894 52         59 my $index = 0;
895 52         86 my %boundhash;
896             my %boundlsids;
897 52         79 foreach my $b (@{$self->{ospf}{database}{boundarys}}) {
  52         169  
898 32         89 my $asbr = $b->{asbrouter};
899 32         48 my $rid = $b->{routerid};
900 32         43 my $area = $b->{area};
901 32         37 my $addr = $b->{address};
902 32         69 $boundlsids{$area}{$rid}{$addr}++;
903 32         54 my $elem = $boundhash{$asbr};
904 32 100       50 if (! $elem) {
905 20         39 $boundhash{$asbr} = $elem = {};
906             $elem->{graph} = {
907 20         79 N => "boundary$index",
908             label => $asbr,
909             shape => "box",
910             style => "dashed",
911             };
912 20         36 $elem->{index} = $index++;
913             }
914 32         39 push @{$elem->{hashes}}, $b;
  32         46  
915 32         70 $elem->{arearids}{$area}{$rid}++;
916             }
917 52         88 $self->{boundhash} = \%boundhash;
918 52         121 $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 65 my OSPF::LSDB::View6 $self = shift;
925 52 50       116 my $routehash = $self->{routehash} or die "Uninitialized member";
926 52 50       124 my $boundhash = $self->{boundhash} or die "Uninitialized member";
927 52 50       97 my $boundlsids = $self->{boundlsids} or die "Uninitialized member";
928 52         52 my @elements;
929 52         52 my $index = 0;
930 52         116 foreach my $asbr (sort keys %$boundhash) {
931 20         30 my $bv = $boundhash->{$asbr};
932 20         22 my $src;
933 20 100       52 if ($bv->{graph}) {
    50          
934 13         20 $src = $bv->{graph}{N};
935             } elsif ($routehash->{$asbr}) {
936             $src = $routehash->{$asbr}{graph}{N}
937 7         15 }
938 20         27 foreach my $b (@{$bv->{hashes}}) {
  20         31  
939 32         40 my $rid = $b->{routerid};
940             my $dst = $routehash->{$rid}{graph}{N}
941 32 50       77 or die "No router graph $rid";
942 32         43 my $addr = $b->{address};
943 32         32 my $area = $b->{area};
944 32         54 my %colors = (gray => $area);
945 32 100 66     88 if ($asbr eq $rid) {
    100          
946             $self->error($colors{brown} =
947 1         7 "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         7 "AS boundary router $asbr is router in same area $area.");
952             }
953 32 100       66 if (! $routehash->{$rid}{areas}{$area}) {
954             $self->error($colors{orange} =
955 2         10 "AS boundary router $asbr and router $rid ".
956             "not in same area $area.");
957             }
958 32 100       57 if ($bv->{arearids}{$area}{$rid} > 1) {
959             $self->error($colors{yellow} =
960 6         23 "AS boundary router $asbr at router $rid ".
961             "has multiple entries in area $area.");
962             }
963 32 100       53 if ($boundlsids->{$area}{$rid}{$addr} > 1) {
964             $self->error($colors{magenta} =
965 4         30 "AS boundary router $asbr at router $rid ".
966             "has multiple link state IDs $addr in area $area.");
967             }
968 32         51 my $metric = $b->{metric};
969             $b->{graph} = {
970 32         101 S => $src,
971             D => $dst,
972             headlabel => $metric,
973             style => "dashed",
974             taillabel => $addr,
975             };
976 32         43 $b->{colors} = \%colors;
977 32         72 $b->{index} = $index++;
978             # in case of aggregation src is undef
979 32 50       69 push @elements, $b if $src;
980             }
981             }
982 52         109 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 65 my OSPF::LSDB::View6 $self = shift;
1010 52         91 my($netcluster) = @_;
1011 52 50       146 my $nets = $self->{nets} or die "Uninitialized member";
1012 52         96 my $sums = $self->{sums};
1013 52 50       119 my $externhash = $self->{externhash} or die "Uninitialized member";
1014 52         152 foreach my $paddr (sort keys %$externhash) {
1015 12         13 my $av = $externhash->{$paddr};
1016 12         21 foreach my $plen (sort keys %$av) {
1017 12         13 my $lv = $av->{$plen};
1018 12         25 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         12 push @{$netcluster->{"$paddr/$plen"}}, $lv->{graph};
  12         39  
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 68 my OSPF::LSDB::View6 $self = shift;
1045 52         79 my $index = 0;
1046 52         69 my %externhash;
1047             my %externlsids;
1048 52         93 foreach my $e (@{$self->{ospf}{database}{externals}}) {
  52         170  
1049 24         29 my $paddr = $e->{prefixaddress};
1050 24         31 my $plen = $e->{prefixlength};
1051 24         28 my $rid = $e->{routerid};
1052 24         25 my $addr = $e->{address};
1053 24         36 $externlsids{$rid}{$addr}++;
1054 24         33 my $elem = $externhash{$paddr}{$plen};
1055 24 100       33 if (! $elem) {
1056 12         29 $externhash{$paddr}{$plen} = $elem = {};
1057             $elem->{graph} = {
1058 12         48 N => "external$index",
1059             label => "$paddr/$plen",
1060             shape => "egg",
1061             style => "solid",
1062             };
1063 12         25 $elem->{index} = $index++;
1064             }
1065 24         24 push @{$elem->{hashes}}, $e;
  24         31  
1066 24         43 $elem->{routers}{$rid}++;
1067             }
1068 52         83 $self->{externhash} = \%externhash;
1069 52         99 $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 86 my OSPF::LSDB::View6 $self = shift;
1076 52 50       105 my $routehash = $self->{routehash} or die "Uninitialized member";
1077 52         66 my $boundhash = $self->{boundhash};
1078 52         76 my $boundaggr = $self->{boundaggr};
1079 52 50       106 my $externhash = $self->{externhash} or die "Uninitialized member";
1080 52 50       104 my $externlsids = $self->{externlsids} or die "Uninitialized member";
1081 52         61 my @elements;
1082 52         64 my $index = 0;
1083 52         132 foreach my $paddr (sort keys %$externhash) {
1084 12         19 my $pv = $externhash->{$paddr};
1085 12         21 foreach my $plen (sort keys %$pv) {
1086 12         14 my $lv = $pv->{$plen};
1087 12         18 my $nid = "$paddr/$plen";
1088 12         16 my $src = $lv->{graph}{N};
1089 12         13 my %dtm; # when dst is aggregated, aggregate edges
1090 12         11 foreach my $e (@{$lv->{hashes}}) {
  12         23  
1091 24         28 my $rid = $e->{routerid};
1092 24         25 my $addr = $e->{address};
1093 24         26 my $type = $e->{type};
1094 24         25 my $metric = $e->{metric};
1095 24         40 my %colors = (gray => "ase");
1096 24 100       38 if ($lv->{routers}{$rid} > 1) {
1097             $self->error($colors{yellow} =
1098 7         21 "AS external network $nid at router $rid ".
1099             "has multiple entries.");
1100             }
1101 24 100       37 if ($externlsids->{$rid}{$addr} > 1) {
1102             $self->error($colors{magenta} =
1103 7         21 "AS external network $nid at router $rid ".
1104             "has multiple link state IDs $addr.");
1105             }
1106 24 100       39 my $style = $type == 1 ? "solid" : "dashed";
1107 24         56 my %graph = (
1108             S => $src,
1109             headlabel => $metric,
1110             style => $style,
1111             taillabel => $addr,
1112             );
1113 24 100       37 if ($routehash->{$rid}) {
1114             my $dst = $routehash->{$rid}{graph}{N}
1115 15 50       32 or die "No router graph $rid";
1116 15         16 $graph{D} = $dst;
1117 15         58 $e->{elems}{$dst} = {
1118             graph => \%graph,
1119             colors => \%colors,
1120             index => $index++,
1121             };
1122 15 50       31 push @elements, $e->{elems}{$dst} if $src;
1123 15         30 next;
1124             }
1125 9         11 my $av = $boundhash->{$rid}{aggregate};
1126 9 50       14 if (! $av) {
1127             my $dst = $boundhash->{$rid}{graph}{N}
1128 9 50       14 or die "No ASB router graph $rid";
1129 9         10 $graph{D} = $dst;
1130 9         25 $e->{elems}{$dst} = {
1131             graph => \%graph,
1132             colors => \%colors,
1133             index => $index++,
1134             };
1135 9 50       17 push @elements, $e->{elems}{$dst} if $src;
1136 9         14 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       36 push @elements, map { values %$_ } map { values %$_ } values %dtm
  0         0  
  0         0  
1157             if $src;
1158             }
1159             }
1160 52         113 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       5 my $lnkhash = $self->{lnkhash} or die "Uninitialized member";
1276 2         3 my %colors;
1277 2         8 while (my($intf,$iv) = each %$lnkhash) {
1278 4         10 while (my($rid, $rv) = each %$iv) {
1279 4         7 my $lid = "$intf\@$rid";
1280 4         13 while (my($area, $av) = each %$rv) {
1281 4         6 $colors{gray} = $area;
1282 4         7 %{$av->{colors}} = %colors;
  4         20  
1283             }
1284             }
1285             }
1286             }
1287              
1288             sub create_link {
1289 2     2 0 6 my OSPF::LSDB::View6 $self = shift;
1290 2         4 my $index = 0;
1291 2         3 my %lnkhash;
1292 2         5 foreach my $l (@{$self->{ospf}{database}{links}}) {
  2         9  
1293 4         9 my $intf = $l->{interface};
1294 4         6 my $rid = $l->{routerid};
1295 4         8 my $lid = "$intf\@$rid";
1296 4         8 my $area = $l->{area};
1297 4         6 my $linklocal = $l->{linklocal};
1298             my $prefixes = join("\\n",
1299 5         18 map { "$_->{prefixaddress}/$_->{prefixlength}" }
1300 4 50       6 @{$l->{prefixes} || []});
  4         11  
1301 4         12 my $elem = $lnkhash{$intf}{$rid}{$area};
1302 4 50       9 if (! $elem) {
1303 4         9 $lnkhash{$intf}{$rid}{$area} = $elem = {};
1304             $elem->{graph} = {
1305 4         22 N => "link$index",
1306             label => "$linklocal\\n$prefixes",
1307             shape => "hexagon",
1308             style => "solid",
1309             };
1310 4         8 $elem->{index} = $index++;
1311             }
1312 4         6 push @{$elem->{hashes}}, $l;
  4         10  
1313             }
1314 2         6 $self->{lnkhash} = \%lnkhash;
1315             }
1316              
1317             # take hash containing link nodes
1318             # return list of nodes
1319             sub link2nodes {
1320 2     2 0 7 my OSPF::LSDB::View6 $self = shift;
1321 2 50       6 my $lnkhash = $self->{lnkhash} or die "Uninitialized member";
1322 2         5 return $self->elements2graphs(map { values %$_ } map { values %$_ }
  4         10  
  4         8  
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       6 my $lnkhash = $self->{lnkhash} or die "Uninitialized member";
1331 2 50       6 my $routehash = $self->{routehash} or die "Uninitialized member";
1332 2 50       5 my $transithash = $self->{transithash} or die "Uninitialized member";
1333 2 50       5 my $transitnets = $self->{transitnets} or die "Uninitialized member";
1334 2 50       5 my $nethash = $self->{nethash} or die "Uninitialized member";
1335 2         4 my @elements;
1336 2         3 my $index = 0;
1337 2         11 foreach my $intf (sort keys %$lnkhash) {
1338 4         6 my $iv = $lnkhash->{$intf};
1339 4         8 foreach my $rid (sort keys %$iv) {
1340 4         6 my $rv = $iv->{$rid};
1341 4         6 my $lid = "$intf\@$rid";
1342             my $rdst = $routehash->{$rid}{graph}{N}
1343 4 50       19 or die "No router graph $rid";
1344 4         8 foreach my $area (sort keys %$rv) {
1345 4         7 my $av = $rv->{$area};
1346 4         6 my $src = $av->{graph}{N};
1347 4         5 my %colors;
1348 4         12 $colors{gray} = $area;
1349 4         51 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       17 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         8 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       12 my $intraroutehash = $self->{intraroutehash} or die "Uninitialized member";
1414 4         6 my %colors;
1415 4         15 while (my($rid, $rv) = each %$intraroutehash) {
1416 3         5 my $iid = "$rid";
1417 3         17 while (my($area, $av) = each %$rv) {
1418 4         7 $colors{gray} = $area;
1419 4         6 %{$av->{colors}} = %colors;
  4         16  
1420             }
1421             }
1422             }
1423              
1424             sub create_intrarouters {
1425 4     4 0 7 my OSPF::LSDB::View6 $self = shift;
1426 4         7 my $index = 0;
1427 4         6 my %intraroutehash;
1428 4         6 foreach my $i (@{$self->{ospf}{database}{intrarouters}}) {
  4         14  
1429 4         7 my $intf = $i->{interface};
1430 4         6 my $rid = $i->{router};
1431 4         5 my $area = $i->{area};
1432 4         8 my $elem = $intraroutehash{$rid}{$area};
1433 4 50       10 if (! $elem) {
1434 4         8 $intraroutehash{$rid}{$area} = $elem = {};
1435             $elem->{graph} = {
1436 4         25 N => "intrarouter$index",
1437             label => "prefixes",
1438             shape => "octagon",
1439             style => "solid",
1440             };
1441 4         10 $elem->{index} = $index++;
1442             }
1443 4         5 push @{$elem->{hashes}}, $i;
  4         8  
1444             $elem->{graph}{label} = join("\\n",
1445 5         26 map { "$_->{prefixaddress}/$_->{prefixlength}" }
1446 4 50       6 map { @{$_->{prefixes} || []} } @{$elem->{hashes}});
  4         4  
  4         14  
  4         7  
1447             }
1448 4         10 $self->{intraroutehash} = \%intraroutehash;
1449             }
1450              
1451             # take hash containing intrarouter nodes
1452             # return list of nodes
1453             sub intrarouter2nodes {
1454 4     4 0 8 my OSPF::LSDB::View6 $self = shift;
1455 4 50       11 my $intraroutehash = $self->{intraroutehash} or die "Uninitialized member";
1456 4         10 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 89 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         7 my $rv = $intraroutehash->{$rid};
1469 3         4 my $iid = "$rid";
1470 3         9 foreach my $area (sort keys %$rv) {
1471 4         7 my $av = $rv->{$area};
1472 4         5 my $src = $av->{graph}{N};
1473             my $dst = $routehash->{$rid}{graph}{N}
1474 4 50       10 or die "No router graph $rid";
1475 4         8 my %colors;
1476 4         170 $colors{gray} = $area;
1477 4         6 foreach my $i (@{$av->{hashes}}) {
  4         9  
1478 4         6 my $addr = $i->{address};
1479 4         26 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         65 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 7 my OSPF::LSDB::View6 $self = shift;
1526 4 50       11 my $intranethash = $self->{intranethash} or die "Uninitialized member";
1527 4         5 my %colors;
1528 4         15 while (my($intf,$iv) = each %$intranethash) {
1529 3         9 while (my($rid, $rv) = each %$iv) {
1530 3         7 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 9 my OSPF::LSDB::View6 $self = shift;
1541 4         6 my $index = 0;
1542 4         7 my %intranethash;
1543 4         9 foreach my $i (@{$self->{ospf}{database}{intranetworks}}) {
  4         16  
1544 4         9 my $intf = $i->{interface};
1545 4         6 my $rid = $i->{router};
1546 4         6 my $area = $i->{area};
1547 4         11 my $elem = $intranethash{$intf}{$rid}{$area};
1548 4 100       7 if (! $elem) {
1549 3         8 $intranethash{$intf}{$rid}{$area} = $elem = {};
1550             $elem->{graph} = {
1551 3         19 N => "intranetwork$index",
1552             label => "prefixes",
1553             shape => "octagon",
1554             style => "bold",
1555             };
1556 3         9 $elem->{index} = $index++;
1557             }
1558 4         7 push @{$elem->{hashes}}, $i;
  4         8  
1559             $elem->{graph}{label} = join("\\n",
1560 6         19 map { "$_->{prefixaddress}/$_->{prefixlength}" }
1561 4 50       5 map { @{$_->{prefixes} || []} } @{$elem->{hashes}});
  5         5  
  5         14  
  4         6  
1562             }
1563 4         12 $self->{intranethash} = \%intranethash;
1564             }
1565              
1566             # take hash containing intranetwork nodes
1567             # return list of nodes
1568             sub intranetwork2nodes {
1569 4     4 0 8 my OSPF::LSDB::View6 $self = shift;
1570 4 50       11 my $intranethash = $self->{intranethash} or die "Uninitialized member";
1571 4         13 return $self->elements2graphs(map { values %$_ } map { values %$_ }
  3         7  
  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 8 my OSPF::LSDB::View6 $self = shift;
1579 4 50       10 my $intranethash = $self->{intranethash} or die "Uninitialized member";
1580 4 50       11 my $nethash = $self->{nethash} or die "Uninitialized member";
1581 4 50       8 my $routehash = $self->{routehash} or die "Uninitialized member";
1582 4         6 my @elements;
1583 4         5 my $index = 0;
1584 4         13 foreach my $intf (sort keys %$intranethash) {
1585 3         6 my $iv = $intranethash->{$intf};
1586 3         6 foreach my $rid (sort keys %$iv) {
1587 3         4 my $rv = $iv->{$rid};
1588 3         5 my $iid = "$intf\@$rid";
1589 3         8 foreach my $area (sort keys %$rv) {
1590 3         4 my $av = $rv->{$area};
1591 3         4 my $src = $av->{graph}{N};
1592             my $dst = $nethash->{$intf}{$rid}{$area}{graph}{N}
1593 3 50       10 or die "No network graph $intf $rid $area";
1594 3         10 my %colors;
1595 3         5 $colors{gray} = $area;
1596 3         4 foreach my $i (@{$av->{hashes}}) {
  3         6  
1597 4         8 my $addr = $i->{address};
1598 4         25 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         2 my $index = 0;
1619 1         9 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         4 foreach (@nodes) {
1634 4         7 $_->{N} = 'router'. $index++;
1635 4   50     14 $_->{shape} ||= 'box';
1636 4   100     12 $_->{style} ||= 'solid';
1637             }
1638              
1639 1         2 my $dot = "";
1640 1         7 $dot .= $class->graph_nodes(@nodes);
1641 1         3 $dot .= "\t{ rank=same;";
1642 1         4 $dot .= join("", map { " $_->{N};" } @nodes);
  4         11  
1643 1         3 $dot .= " }\n";
1644 1         5 return $dot;
1645             }
1646              
1647             # return legend networks as dot graph
1648             sub legend_network {
1649 1     1 0 3 my $class = shift;
1650 1         3 my $index = 0;
1651 1         9 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         3 foreach (@nodes) {
1671 5         9 $_->{N} = 'network'. $index++;
1672 5   100     12 $_->{shape} ||= 'ellipse';
1673 5   100     12 $_->{style} ||= 'solid';
1674             }
1675              
1676 1         4 my $dot = "";
1677 1         3 $dot .= $class->graph_nodes(@nodes);
1678 1         2 $dot .= "\t{ rank=same;";
1679 1         4 $dot .= join("", map { " $_->{N};" } @nodes);
  5         10  
1680 1         3 $dot .= " }\n";
1681 1         7 return $dot;
1682             }
1683              
1684             # return legend router network edges as dot graph
1685             sub legend_edge {
1686 1     1 0 3 my $class = shift;
1687 1         11 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     15 $_->{shape} ||= 'ellipse';
1711 6   100     14 $_->{style} ||= 'solid';
1712             }
1713              
1714 1         7 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     16 $_->{shape} ||= 'box';
1729 5   50     14 $_->{style} ||= 'solid';
1730             }
1731              
1732 1         3 my $index = 0;
1733 1         13 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         11 $networknodes[$i]{N} = 'edgenetwork'. $index;
1760 6         11 $routernodes [$i]{N} = 'edgerouter'. $index;
1761 6         11 $edges [$i]{S} = 'edgenetwork'. $index;
1762 6         12 $edges [$i]{D} = 'edgerouter'. $index;
1763 6         10 $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         4 $edges[-1]{D} = $edges[-2]{D};
1769 1         3 pop @routernodes;
1770              
1771 1         3 my $dot = "";
1772 1         5 $dot .= $class->graph_nodes(@networknodes);
1773 1         4 $dot .= $class->graph_nodes(@routernodes);
1774 1         10 $dot .= $class->graph_edges(@edges);
1775 1         4 $dot .= "\t{ rank=same;";
1776 1         3 $dot .= join("", map { " $_->{S};" } @edges);
  6         12  
1777 1         3 $dot .= " }\n";
1778 1         13 return $dot;
1779             }
1780              
1781             # return legend router link to router or network as dot graph
1782             sub legend_link {
1783 1     1 0 3 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     14 $_->{label} ||= 'router';
1797 6   100     14 $_->{shape} ||= 'box';
1798 6   50     14 $_->{style} ||= 'solid';
1799             }
1800              
1801 1         6 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     21 $_->{shape} ||= 'box';
      100        
1819 5   100     9 $_->{style} ||= 'solid';
1820             }
1821              
1822 1         2 my $index = 0;
1823 1         10 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         4 foreach (@edges) {
1842 6   100     14 $_->{style} ||= 'solid';
1843             }
1844 1         4 for(my $i=0; $i<@edges; $i++) {
1845 6         11 $routernodes[$i]{N} = 'linkrouter'. $index;
1846 6         10 $dstnodes [$i]{N} = 'linkdst'. $index;
1847 6         10 $edges [$i]{S} = 'linkrouter'. $index;
1848 6         11 $edges [$i]{D} = 'linkdst'. $index;
1849 6         10 $index++;
1850             }
1851             # link and intra area prefix have same network destination
1852 1         3 $edges[-1]{D} = $edges[-2]{D};
1853 1         2 pop @dstnodes;
1854              
1855 1         3 my $dot = "";
1856 1         4 $dot .= $class->graph_nodes(@routernodes);
1857 1         4 $dot .= $class->graph_nodes(@dstnodes);
1858 1         4 $dot .= $class->graph_edges(@edges);
1859 1         3 $dot .= "\t{ rank=same;";
1860 1         4 $dot .= join("", map { " $_->{S};" } @edges);
  6         11  
1861 1         4 $dot .= " }\n";
1862 1         13 return $dot;
1863             }
1864              
1865             # return legend summary network and router edges as dot graph
1866             sub legend_summary {
1867 1     1 0 3 my $class = shift;
1868 1         8 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     10 $_->{shape} ||= 'ellipse';
1887 4   100     9 $_->{style} ||= 'solid';
1888             }
1889              
1890 1         24 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     22 $_->{label} ||= 'area border\nrouter';
1901 4   50     12 $_->{shape} ||= 'box';
1902 4   100     11 $_->{style} ||= 'bold';
1903             }
1904              
1905 1         3 my $index = 0;
1906 1         9 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         5 for(my $i=0; $i<@edges; $i++) {
1928 4         7 $networknodes[$i]{N} = 'summarynetwork'. $index;
1929 4         7 $routernodes [$i]{N} = 'summaryrouter'. $index;
1930 4         5 $edges [$i]{S} = 'summarynetwork'. $index;
1931 4         9 $edges [$i]{D} = 'summaryrouter'. $index;
1932 4         8 $index++;
1933             }
1934              
1935 1         2 my $dot = "";
1936 1         5 $dot .= $class->graph_nodes(@networknodes);
1937 1         4 $dot .= $class->graph_nodes(@routernodes);
1938 1         4 $dot .= $class->graph_edges(@edges);
1939 1         3 $dot .= "\t{ rank=same;";
1940 1         3 $dot .= join("", map { " $_->{S};" } @edges);
  4         10  
1941 1         3 $dot .= " }\n";
1942 1         12 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;