| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | ########################################################################## | 
| 2 |  |  |  |  |  |  | # Copyright (c) 2010-2022 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 | 18 |  |  | 18 |  | 478892 | use strict; | 
|  | 18 |  |  |  |  | 85 |  | 
|  | 18 |  |  |  |  | 437 |  | 
| 18 | 18 |  |  | 18 |  | 71 | use warnings; | 
|  | 18 |  |  |  |  | 25 |  | 
|  | 18 |  |  |  |  | 731 |  | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | =pod | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | =head1 NAME | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | OSPF::LSDB::ospfd - parse OpenBSD B link state database | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | use OSPF::LSDB::ospfd; | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | my $ospfd = OSPF::LSDB::ospfd-Enew(); | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | my $ospfd = OSPF::LSDB::ospfd-Enew(ssh => "user@host"); | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | $ospfd-Eparse(%files); | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | The OSPF::LSDB::ospfd module parses the output of OpenBSD B | 
| 39 |  |  |  |  |  |  | and fills the L base object. | 
| 40 |  |  |  |  |  |  | The output of | 
| 41 |  |  |  |  |  |  | C, | 
| 42 |  |  |  |  |  |  | C, | 
| 43 |  |  |  |  |  |  | C, | 
| 44 |  |  |  |  |  |  | C, | 
| 45 |  |  |  |  |  |  | C, | 
| 46 |  |  |  |  |  |  | C | 
| 47 |  |  |  |  |  |  | is needed. | 
| 48 |  |  |  |  |  |  | It can be given as separate files or obtained dynamically. | 
| 49 |  |  |  |  |  |  | In the latter case B is invoked if permissions are not | 
| 50 |  |  |  |  |  |  | sufficient to run B. | 
| 51 |  |  |  |  |  |  | If the object has been created with the C argument, the specified | 
| 52 |  |  |  |  |  |  | user and host are used to login and run B there. | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | There is only one public method: | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | =cut | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | package OSPF::LSDB::ospfd; | 
| 59 | 18 |  |  | 18 |  | 76 | use base 'OSPF::LSDB'; | 
|  | 18 |  |  |  |  | 33 |  | 
|  | 18 |  |  |  |  | 6581 |  | 
| 60 | 18 |  |  | 18 |  | 5875 | use File::Slurp; | 
|  | 18 |  |  |  |  | 215032 |  | 
|  | 18 |  |  |  |  | 1079 |  | 
| 61 | 18 |  |  | 18 |  | 115 | use Regexp::Common; | 
|  | 18 |  |  |  |  | 31 |  | 
|  | 18 |  |  |  |  | 172 |  | 
| 62 | 18 |  |  |  |  | 147 | use fields qw( | 
| 63 |  |  |  |  |  |  | ospfd ospfctl ospfsock showdb | 
| 64 |  |  |  |  |  |  | selfid | 
| 65 |  |  |  |  |  |  | router network summary boundary external | 
| 66 | 18 |  |  | 18 |  | 430773 | ); | 
|  | 18 |  |  |  |  | 36 |  | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | sub new { | 
| 69 | 16 |  |  | 16 | 1 | 706 | my OSPF::LSDB::ospfd $self = OSPF::LSDB::new(@_); | 
| 70 | 16 |  |  |  |  | 42 | $self->{ospfd} = "ospfd"; | 
| 71 | 16 |  |  |  |  | 43 | $self->{ospfctl} = "ospfctl"; | 
| 72 |  |  |  |  |  |  | # XXX hard coded routing domain 0 | 
| 73 | 16 |  |  |  |  | 38 | $self->{ospfsock} = "/var/run/ospfd.sock.0"; | 
| 74 |  |  |  |  |  |  | $self->{showdb} = { | 
| 75 | 16 |  |  |  |  | 84 | router   => "router", | 
| 76 |  |  |  |  |  |  | network  => "network", | 
| 77 |  |  |  |  |  |  | summary  => "summary", | 
| 78 |  |  |  |  |  |  | boundary => "asbr", | 
| 79 |  |  |  |  |  |  | external => "external", | 
| 80 |  |  |  |  |  |  | }; | 
| 81 | 16 |  |  |  |  | 49 | return $self; | 
| 82 |  |  |  |  |  |  | } | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | # shortcut | 
| 85 |  |  |  |  |  |  | my $IP = qr/$RE{net}{IPv4}{-keep}/; | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | sub ospfctl_show { | 
| 88 | 0 |  |  | 0 | 0 | 0 | my OSPF::LSDB::ospfd $self = shift; | 
| 89 | 0 |  |  |  |  | 0 | my @cmd = ($self->{ospfctl}, "show", @_); | 
| 90 | 0 | 0 |  |  |  | 0 | if ($self->{ssh}) { | 
|  |  | 0 |  |  |  |  |  | 
| 91 | 0 |  |  |  |  | 0 | unshift @cmd, "ssh", $self->{ssh}; | 
| 92 |  |  |  |  |  |  | } elsif (-S $self->{ospfsock}) { | 
| 93 |  |  |  |  |  |  | # no doas if user is root or in wheel group | 
| 94 |  |  |  |  |  |  | # srw-rw----  1 root  wheel  0 Jun 13 10:10 /var/run/ospfd.sock | 
| 95 | 0 | 0 |  |  |  | 0 | unshift @cmd, "doas" unless -w $self->{ospfsock}; | 
| 96 |  |  |  |  |  |  | } else { | 
| 97 | 0 |  |  |  |  | 0 | die "Control socket '$self->{ospfsock}' for $self->{ospfd} missing.\n"; | 
| 98 |  |  |  |  |  |  | } | 
| 99 | 0 | 0 |  |  |  | 0 | open(my $fh, '-|', @cmd) | 
| 100 |  |  |  |  |  |  | or die "Open pipe from '@cmd' failed: $!"; | 
| 101 | 0 |  |  |  |  | 0 | my @lines = $fh->getlines(); | 
| 102 | 0 | 0 |  |  |  | 0 | close($fh) or die $! ? | 
|  |  | 0 |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | "Close pipe from '@cmd' failed: $!" : | 
| 104 |  |  |  |  |  |  | "Command '@cmd' failed: $?\n"; | 
| 105 | 0 | 0 |  |  |  | 0 | return wantarray ? @lines : join("", @lines); | 
| 106 |  |  |  |  |  |  | } | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | sub read_files { | 
| 109 | 4 |  |  | 4 | 0 | 7 | my OSPF::LSDB::ospfd $self = shift; | 
| 110 | 4 |  |  |  |  | 13 | my %files = @_; | 
| 111 | 4 |  |  |  |  | 8 | my $file = $files{selfid}; | 
| 112 | 4 | 50 |  |  |  | 31 | my @lines = $file ? read_file($file) : $self->ospfctl_show("summary"); | 
| 113 | 4 |  |  |  |  | 668 | $self->{selfid} = \@lines; | 
| 114 | 4 |  |  |  |  | 10 | foreach (sort keys %{$self->{showdb}}) { | 
|  | 4 |  |  |  |  | 27 |  | 
| 115 | 24 |  |  |  |  | 42 | my $file = $files{$_}; | 
| 116 |  |  |  |  |  |  | my @lines = $file ? read_file($file) : | 
| 117 | 24 | 50 |  |  |  | 80 | $self->ospfctl_show("database", $self->{showdb}{$_}); | 
| 118 | 24 |  |  |  |  | 5450 | $self->{$_} = \@lines; | 
| 119 |  |  |  |  |  |  | } | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | sub parse_self { | 
| 123 | 6 |  |  | 6 | 0 | 49 | my OSPF::LSDB::ospfd $self = shift; | 
| 124 | 6 |  |  |  |  | 11 | my($routerid, @areas); | 
| 125 | 6 |  |  |  |  | 11 | foreach (@{$self->{selfid}}) { | 
|  | 6 |  |  |  |  | 21 |  | 
| 126 | 99 | 100 |  |  |  | 872 | if (/^Router ID: $IP$/) { | 
|  |  | 100 |  |  |  |  |  | 
| 127 | 6 |  |  |  |  | 32 | $routerid = $1; | 
| 128 |  |  |  |  |  |  | } elsif (/^Area ID: $IP$/) { | 
| 129 | 10 |  |  |  |  | 31 | push @areas, $1; | 
| 130 |  |  |  |  |  |  | } | 
| 131 |  |  |  |  |  |  | } | 
| 132 | 6 |  |  |  |  | 35 | $self->{ospf}{self} = { routerid => $routerid, areas => \@areas }; | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | sub parse_router { | 
| 136 | 16 |  |  | 16 | 0 | 10478 | my OSPF::LSDB::ospfd $self = shift; | 
| 137 | 16 |  |  |  |  | 32 | my($area, $router, $link) = ("", "", ""); | 
| 138 | 16 |  |  |  |  | 26 | my(@routers, $lnum, $type, $r, $l); | 
| 139 | 16 |  |  |  |  | 17 | foreach (@{$self->{router}}) { | 
|  | 16 |  |  |  |  | 31 |  | 
| 140 | 1150 | 100 | 100 |  |  | 65367 | if (/^ +Router Link States \(Area $IP\)$/) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 141 | 22 | 100 |  |  |  | 73 | die "$_ Link $link of router $router in area $area not finished.\n" | 
| 142 |  |  |  |  |  |  | if $l; | 
| 143 | 21 | 100 |  |  |  | 42 | die "$_ Too few links at router $router in area $area.\n" if $lnum; | 
| 144 | 20 | 100 |  |  |  | 33 | die "$_ Router $router in area $area not finished.\n" if $r; | 
| 145 | 19 |  |  |  |  | 35 | $area = $1; | 
| 146 | 19 |  |  |  |  | 27 | next; | 
| 147 |  |  |  |  |  |  | } elsif (/^$/) { | 
| 148 | 193 | 100 |  |  |  | 311 | if ($l) { | 
| 149 | 105 | 100 |  |  |  | 159 | die "Link $link of router $router in area $area without type.\n" | 
| 150 |  |  |  |  |  |  | if ! $type; | 
| 151 | 104 |  |  |  |  | 95 | push @{$r->{$type.'s'}}, $l; | 
|  | 104 |  |  |  |  | 233 |  | 
| 152 | 104 |  |  |  |  | 131 | undef $type; | 
| 153 | 104 |  |  |  |  | 119 | undef $l; | 
| 154 |  |  |  |  |  |  | } | 
| 155 | 192 | 100 |  |  |  | 257 | if (! $lnum) { | 
| 156 | 81 |  |  |  |  | 96 | undef $r; | 
| 157 |  |  |  |  |  |  | } | 
| 158 | 192 |  |  |  |  | 372 | next; | 
| 159 |  |  |  |  |  |  | } elsif (! $r && /^\w/) { | 
| 160 | 53 | 100 |  |  |  | 98 | die "$_ No area for router defined.\n" if ! $area; | 
| 161 | 52 |  |  |  |  | 66 | $router = ""; | 
| 162 | 52 |  |  |  |  | 96 | $r = { area => $area }; | 
| 163 | 52 |  |  |  |  | 67 | push @routers, $r; | 
| 164 |  |  |  |  |  |  | } elsif (! $l && /^ {4}\w/) { | 
| 165 | 110 | 100 |  |  |  | 202 | die "$_ Too many links at router $router in area $area.\n" | 
| 166 |  |  |  |  |  |  | if ! $lnum; | 
| 167 | 109 |  |  |  |  | 123 | $lnum--; | 
| 168 | 109 |  |  |  |  | 134 | $link = ""; | 
| 169 | 109 |  |  |  |  | 131 | $l = {}; | 
| 170 |  |  |  |  |  |  | } | 
| 171 | 933 | 100 |  |  |  | 2727 | if (/^LS age: $RE{num}{int}{-keep}$/) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 172 | 52 |  |  |  |  | 5522 | $r->{age} = $1; | 
| 173 |  |  |  |  |  |  | } elsif (/^LS Type: ([\w ()]+)$/) { | 
| 174 | 52 | 100 |  |  |  | 5058 | die "$_ Type of router-LSA is $1 and not Router in area $area.\n" | 
| 175 |  |  |  |  |  |  | if $1 ne "Router"; | 
| 176 |  |  |  |  |  |  | } elsif (/^Link State ID: $IP$/) { | 
| 177 | 51 |  |  |  |  | 4925 | $r->{router} = $1; | 
| 178 | 51 |  |  |  |  | 255 | $router = $1; | 
| 179 |  |  |  |  |  |  | } elsif (/^Advertising Router: $IP$/) { | 
| 180 | 51 |  |  |  |  | 5094 | $r->{routerid} = $1; | 
| 181 |  |  |  |  |  |  | } elsif (/^LS Seq Number: (0x$RE{num}{hex})$/) { | 
| 182 | 51 |  |  |  |  | 9620 | $r->{sequence} = $1; | 
| 183 |  |  |  |  |  |  | } elsif (/^Flags: ([-*|\w]*)$/) { | 
| 184 | 48 |  |  |  |  | 8864 | my $flags = $1; | 
| 185 | 48 |  |  |  |  | 86 | foreach (qw(V E B)) { | 
| 186 | 144 | 100 |  |  |  | 1251 | $r->{bits}{$_} = $flags =~ /\b$_\b/ ? 1 : 0; | 
| 187 |  |  |  |  |  |  | } | 
| 188 |  |  |  |  |  |  | } elsif (/^Number of Links: $RE{num}{int}{-keep}$/) { | 
| 189 | 48 |  |  |  |  | 13894 | $lnum = $1; | 
| 190 |  |  |  |  |  |  | } elsif (/^    Link connected to: ([\w -]+)$/) { | 
| 191 | 108 | 50 |  |  |  | 29944 | if ($1 eq "Point-to-Point") { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 192 | 0 |  |  |  |  | 0 | $type = "pointtopoint"; | 
| 193 |  |  |  |  |  |  | } elsif ($1 eq "Transit Network") { | 
| 194 | 79 |  |  |  |  | 798 | $type = "transit"; | 
| 195 |  |  |  |  |  |  | } elsif ($1 eq "Stub Network") { | 
| 196 | 28 |  |  |  |  | 244 | $type = "stub"; | 
| 197 |  |  |  |  |  |  | } elsif ($1 eq "Virtual Link") { | 
| 198 | 0 |  |  |  |  | 0 | $type = "virtual"; | 
| 199 |  |  |  |  |  |  | } else { | 
| 200 | 1 |  |  |  |  | 10 | die "$_ Unknown link type $1 at router $router ". | 
| 201 |  |  |  |  |  |  | "in area $area.\n"; | 
| 202 |  |  |  |  |  |  | } | 
| 203 |  |  |  |  |  |  | } elsif (/^    Link ID \(Neighbors Router ID\): $IP$/) { | 
| 204 | 0 |  |  |  |  | 0 | $l->{routerid} = $1; | 
| 205 | 0 |  |  |  |  | 0 | $link = $1; | 
| 206 |  |  |  |  |  |  | } elsif (/^    Link ID \(Designated Router address\): $IP$/) { | 
| 207 | 79 |  |  |  |  | 22113 | $l->{address} = $1; | 
| 208 | 79 |  |  |  |  | 743 | $link = $1; | 
| 209 |  |  |  |  |  |  | } elsif (/^    Link ID \(Network ID\): $IP$/) { | 
| 210 | 29 |  |  |  |  | 7930 | $l->{network} = $1; | 
| 211 | 29 |  |  |  |  | 268 | $link = $1; | 
| 212 |  |  |  |  |  |  | } elsif (/^    Link Data \(Router Interface address\): $IP$/) { | 
| 213 | 79 |  |  |  |  | 22894 | $l->{interface} = $1; | 
| 214 |  |  |  |  |  |  | } elsif (/^    Link Data \(Network Mask\): $IP$/) { | 
| 215 | 29 |  |  |  |  | 8531 | $l->{netmask} = $1; | 
| 216 |  |  |  |  |  |  | } elsif (/^    Metric: $RE{num}{int}{-keep}$/) { | 
| 217 | 105 |  |  |  |  | 40663 | $l->{metric} = $1; | 
| 218 |  |  |  |  |  |  | } elsif (/^ {4}\w/) { | 
| 219 | 1 |  |  |  |  | 439 | die "$_ Unknown line at link $link of router $router ". | 
| 220 |  |  |  |  |  |  | "in area $area.\n"; | 
| 221 |  |  |  |  |  |  | } elsif (! /^(Options|Checksum|Length):/) { | 
| 222 | 1 |  |  |  |  | 370 | die "$_ Unknown line at router $router in area $area.\n"; | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  | } | 
| 225 | 6 | 100 |  |  |  | 31 | die "Link $link of router $router in area $area not finished.\n" if $l; | 
| 226 | 5 | 100 |  |  |  | 22 | die "Too few links at router $router in area $area.\n" if $lnum; | 
| 227 | 4 | 100 |  |  |  | 18 | die "Router $router in area $area not finished.\n" if $r; | 
| 228 | 3 |  |  |  |  | 19 | $self->{ospf}{database}{routers} = \@routers; | 
| 229 |  |  |  |  |  |  | } | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | sub parse_network { | 
| 232 | 11 |  |  | 11 | 0 | 7246 | my OSPF::LSDB::ospfd $self = shift; | 
| 233 | 11 |  |  |  |  | 27 | my($area, $network) = ("", ""); | 
| 234 | 11 |  |  |  |  | 14 | my(@networks, $attachments, $rnum, $n); | 
| 235 | 11 |  |  |  |  | 13 | foreach (@{$self->{network}}) { | 
|  | 11 |  |  |  |  | 25 |  | 
| 236 | 518 | 100 |  |  |  | 36262 | if (/^ +Net Link States \(Area $IP\)$/) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 237 | 16 | 100 |  |  |  | 39 | die "$_ Attached routers of network $network in area $area ". | 
| 238 |  |  |  |  |  |  | "not finished.\n" if $attachments; | 
| 239 | 15 | 100 |  |  |  | 31 | die "$_ Network $network in area $area not finished.\n" if $n; | 
| 240 | 14 |  |  |  |  | 28 | $area = $1; | 
| 241 | 14 |  |  |  |  | 20 | next; | 
| 242 |  |  |  |  |  |  | } elsif (/^$/) { | 
| 243 | 64 | 100 |  |  |  | 108 | die "$_ Too few attached routers at network $network ". | 
| 244 |  |  |  |  |  |  | "in area $area.\n" if $rnum; | 
| 245 | 63 |  |  |  |  | 67 | undef $attachments; | 
| 246 | 63 |  |  |  |  | 61 | undef $n; | 
| 247 | 63 |  |  |  |  | 97 | next; | 
| 248 |  |  |  |  |  |  | } elsif (! $n) { | 
| 249 | 41 | 100 |  |  |  | 104 | die "$_ No area for network defined.\n" if ! $area; | 
| 250 | 40 |  |  |  |  | 47 | $network = ""; | 
| 251 | 40 |  |  |  |  | 68 | $n = { area => $area }; | 
| 252 | 40 |  |  |  |  | 63 | push @networks, $n; | 
| 253 |  |  |  |  |  |  | } | 
| 254 | 437 | 100 |  |  |  | 1265 | if (/^LS age: $RE{num}{int}{-keep}$/) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 255 | 40 |  |  |  |  | 4086 | $n->{age} = $1; | 
| 256 |  |  |  |  |  |  | } elsif (/^LS Type: ([\w ()]+)$/) { | 
| 257 | 40 | 100 |  |  |  | 4021 | die "$_ Type of network-LSA is $1 and not Network in area $area.\n" | 
| 258 |  |  |  |  |  |  | if $1 ne "Network"; | 
| 259 |  |  |  |  |  |  | } elsif (/^Link State ID: $IP \(address of Designated Router\)$/) { | 
| 260 | 39 |  |  |  |  | 3826 | $n->{address} = $1; | 
| 261 | 39 |  |  |  |  | 188 | $network = $1; | 
| 262 |  |  |  |  |  |  | } elsif (/^Advertising Router: $IP$/) { | 
| 263 | 39 |  |  |  |  | 3794 | $n->{routerid} = $1; | 
| 264 |  |  |  |  |  |  | } elsif (/^LS Seq Number: (0x$RE{num}{hex})$/) { | 
| 265 | 39 |  |  |  |  | 8353 | $n->{sequence} = $1; | 
| 266 |  |  |  |  |  |  | } elsif (/^Network Mask: $IP$/) { | 
| 267 | 36 |  |  |  |  | 6943 | $n->{netmask} = $1; | 
| 268 |  |  |  |  |  |  | } elsif (/^Number of Routers: $RE{num}{int}{-keep}$/) { | 
| 269 | 4 |  |  |  |  | 1084 | $rnum = $1; | 
| 270 |  |  |  |  |  |  | } elsif (/^    Attached Router: $IP$/) { | 
| 271 | 86 | 100 |  |  |  | 24159 | if (! $attachments) { | 
| 272 | 36 |  |  |  |  | 57 | $attachments = []; | 
| 273 | 36 |  |  |  |  | 57 | $n->{attachments} = $attachments; | 
| 274 |  |  |  |  |  |  | } | 
| 275 | 86 | 100 |  |  |  | 135 | $rnum-- if defined $rnum ; | 
| 276 | 86 |  |  |  |  | 924 | push @$attachments, { routerid => $1 }; | 
| 277 |  |  |  |  |  |  | } elsif (! /^(Options|Checksum|Length):/) { | 
| 278 | 1 |  |  |  |  | 280 | die "$_ Unknown line at network $network in area $area.\n"; | 
| 279 |  |  |  |  |  |  | } | 
| 280 |  |  |  |  |  |  | } | 
| 281 | 5 | 100 |  |  |  | 22 | die "Attached routers of network $network in area $area not finished.\n" | 
| 282 |  |  |  |  |  |  | if $attachments; | 
| 283 | 4 | 100 |  |  |  | 16 | die "Network $network in area $area not finished.\n" if $n; | 
| 284 | 3 |  |  |  |  | 15 | $self->{ospf}{database}{networks} = \@networks; | 
| 285 |  |  |  |  |  |  | } | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | sub parse_summary { | 
| 288 | 8 |  |  | 8 | 0 | 4355 | my OSPF::LSDB::ospfd $self = shift; | 
| 289 | 8 |  |  |  |  | 17 | my($area, $summary) = ("", ""); | 
| 290 | 8 |  |  |  |  | 12 | my(@summarys, $s); | 
| 291 | 8 |  |  |  |  | 11 | foreach (@{$self->{summary}}) { | 
|  | 8 |  |  |  |  | 19 |  | 
| 292 | 1391 | 100 |  |  |  | 110524 | if (/^ +Summary Net Link States \(Area $IP\)$/) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 293 | 12 | 100 |  |  |  | 39 | die "$_ Summary $summary in area $area not finished.\n" if $s; | 
| 294 | 11 |  |  |  |  | 32 | $area = $1; | 
| 295 | 11 |  |  |  |  | 20 | next; | 
| 296 |  |  |  |  |  |  | } elsif (/^$/) { | 
| 297 | 145 |  |  |  |  | 173 | undef $s; | 
| 298 | 145 |  |  |  |  | 238 | next; | 
| 299 |  |  |  |  |  |  | } elsif (! $s) { | 
| 300 | 126 | 100 |  |  |  | 208 | die "$_ No area for summary defined.\n" if ! $area; | 
| 301 | 125 |  |  |  |  | 133 | $summary = ""; | 
| 302 | 125 |  |  |  |  | 186 | $s = { area => $area }; | 
| 303 | 125 |  |  |  |  | 191 | push @summarys, $s; | 
| 304 |  |  |  |  |  |  | } | 
| 305 | 1233 | 100 |  |  |  | 3433 | if (/^LS age: $RE{num}{int}{-keep}$/) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 306 | 125 |  |  |  |  | 11956 | $s->{age} = $1; | 
| 307 |  |  |  |  |  |  | } elsif (/^LS Type: ([\w ()]+)$/) { | 
| 308 | 125 | 100 |  |  |  | 12900 | die "$_ Type of summary-LSA is $1 and not Summary (Network) ". | 
| 309 |  |  |  |  |  |  | "in area $area.\n" if $1 ne "Summary (Network)"; | 
| 310 |  |  |  |  |  |  | } elsif (/^Link State ID: $IP \(Network ID\)$/) { | 
| 311 | 124 |  |  |  |  | 11982 | $s->{address} = $1; | 
| 312 | 124 |  |  |  |  | 565 | $summary = $1; | 
| 313 |  |  |  |  |  |  | } elsif (/^Advertising Router: $IP$/) { | 
| 314 | 124 |  |  |  |  | 12757 | $s->{routerid} = $1; | 
| 315 |  |  |  |  |  |  | } elsif (/^LS Seq Number: (0x$RE{num}{hex})$/) { | 
| 316 | 124 |  |  |  |  | 22904 | $s->{sequence} = $1; | 
| 317 |  |  |  |  |  |  | } elsif (/^Network Mask: $IP$/) { | 
| 318 | 121 |  |  |  |  | 23408 | $s->{netmask} = $1; | 
| 319 |  |  |  |  |  |  | } elsif (/^Metric: $RE{num}{int}{-keep}$/) { | 
| 320 | 121 |  |  |  |  | 33779 | $s->{metric} = $1; | 
| 321 |  |  |  |  |  |  | } elsif (! /^(Options|Checksum|Length):/) { | 
| 322 | 1 |  |  |  |  | 279 | die "$_ Unknown line at summary $summary in area $area.\n"; | 
| 323 |  |  |  |  |  |  | } | 
| 324 |  |  |  |  |  |  | } | 
| 325 | 4 | 100 |  |  |  | 17 | die "Summary $summary in area $area not finished.\n" if $s; | 
| 326 | 3 |  |  |  |  | 15 | $self->{ospf}{database}{summarys} = \@summarys; | 
| 327 |  |  |  |  |  |  | } | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | sub parse_boundary { | 
| 330 | 8 |  |  | 8 | 0 | 4522 | my OSPF::LSDB::ospfd $self = shift; | 
| 331 | 8 |  |  |  |  | 19 | my($area, $boundary) = ("", ""); | 
| 332 | 8 |  |  |  |  | 11 | my(@boundarys, $b); | 
| 333 | 8 |  |  |  |  | 11 | foreach (@{$self->{boundary}}) { | 
|  | 8 |  |  |  |  | 18 |  | 
| 334 | 731 | 100 |  |  |  | 74978 | if (/^ +Summary Router Link States \(Area $IP\)$/) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 335 | 12 | 100 |  |  |  | 36 | die "$_ Boundary $boundary in area $area not finished.\n" if $b; | 
| 336 | 11 |  |  |  |  | 22 | $area = $1; | 
| 337 | 11 |  |  |  |  | 15 | next; | 
| 338 |  |  |  |  |  |  | } elsif (/^$/) { | 
| 339 | 85 |  |  |  |  | 100 | undef $b; | 
| 340 | 85 |  |  |  |  | 140 | next; | 
| 341 |  |  |  |  |  |  | } elsif (! $b) { | 
| 342 | 66 | 100 |  |  |  | 102 | die "$_ No area for boundary defined.\n" if ! $area; | 
| 343 | 65 |  |  |  |  | 72 | $boundary = ""; | 
| 344 | 65 |  |  |  |  | 115 | $b = { area => $area }; | 
| 345 | 65 |  |  |  |  | 176 | push @boundarys, $b; | 
| 346 |  |  |  |  |  |  | } | 
| 347 | 633 | 100 |  |  |  | 1791 | if (/^LS age: $RE{num}{int}{-keep}$/) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 348 | 65 |  |  |  |  | 6938 | $b->{age} = $1; | 
| 349 |  |  |  |  |  |  | } elsif (/^LS Type: ([\w ()]+)$/) { | 
| 350 | 65 | 100 |  |  |  | 6442 | die "$_ Type of boundary-LSA is $1 and not Summary (Router) ". | 
| 351 |  |  |  |  |  |  | "in area $area.\n" if $1 ne "Summary (Router)"; | 
| 352 |  |  |  |  |  |  | } elsif (/^Link State ID: $IP \(ASBR Router ID\)$/) { | 
| 353 | 64 |  |  |  |  | 6420 | $b->{asbrouter} = $1; | 
| 354 | 64 |  |  |  |  | 298 | $boundary = $1; | 
| 355 |  |  |  |  |  |  | } elsif (/^Advertising Router: $IP$/) { | 
| 356 | 64 |  |  |  |  | 6268 | $b->{routerid} = $1; | 
| 357 |  |  |  |  |  |  | } elsif (/^LS Seq Number: (0x$RE{num}{hex})$/) { | 
| 358 | 64 |  |  |  |  | 12281 | $b->{sequence} = $1; | 
| 359 |  |  |  |  |  |  | } elsif (/^Metric: $RE{num}{int}{-keep}$/) { | 
| 360 | 61 |  |  |  |  | 17054 | $b->{metric} = $1; | 
| 361 |  |  |  |  |  |  | } elsif (! /^(Options|Checksum|Length|Network Mask):/) { | 
| 362 | 1 |  |  |  |  | 267 | die "$_ Unknown line at boundary $boundary in area $area.\n"; | 
| 363 |  |  |  |  |  |  | } | 
| 364 |  |  |  |  |  |  | } | 
| 365 | 4 | 100 |  |  |  | 20 | die "Boundary $boundary in area $area not finished.\n" if $b; | 
| 366 | 3 |  |  |  |  | 16 | $self->{ospf}{database}{boundarys} = \@boundarys; | 
| 367 |  |  |  |  |  |  | } | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | sub parse_external { | 
| 370 | 8 |  |  | 8 | 0 | 4457 | my OSPF::LSDB::ospfd $self = shift; | 
| 371 | 8 |  |  |  |  | 13 | my $external = ""; | 
| 372 | 8 |  |  |  |  | 10 | my(@externals, $e); | 
| 373 | 8 |  |  |  |  | 11 | foreach (@{$self->{external}}) { | 
|  | 8 |  |  |  |  | 21 |  | 
| 374 | 1981 | 100 |  |  |  | 164958 | if (/^ +Type-5 AS External Link States$/) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 375 | 10 | 100 |  |  |  | 28 | die "$_ External $external not finished.\n" if $e; | 
| 376 | 9 | 100 |  |  |  | 25 | die "$_ Too many external sections.\n", if @externals; | 
| 377 | 8 |  |  |  |  | 14 | next; | 
| 378 |  |  |  |  |  |  | } elsif (/^$/) { | 
| 379 | 154 |  |  |  |  | 173 | undef $e; | 
| 380 | 154 |  |  |  |  | 204 | next; | 
| 381 |  |  |  |  |  |  | } elsif (! $e) { | 
| 382 | 142 |  |  |  |  | 187 | $external = ""; | 
| 383 | 142 |  |  |  |  | 171 | $e = {}; | 
| 384 | 142 |  |  |  |  | 209 | push @externals, $e; | 
| 385 |  |  |  |  |  |  | } | 
| 386 | 1817 | 100 |  |  |  | 5058 | if (/^LS age: $RE{num}{int}{-keep}$/) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 387 | 142 |  |  |  |  | 14141 | $e->{age} = $1; | 
| 388 |  |  |  |  |  |  | } elsif (/^LS Type: ([\w ()]+)$/) { | 
| 389 | 142 | 100 |  |  |  | 14522 | die "$_ Type of external-LSA is $1 and not AS External.\n" | 
| 390 |  |  |  |  |  |  | if $1 ne "AS External"; | 
| 391 |  |  |  |  |  |  | } elsif (/^Link State ID: $IP \(External Network Number\)$/) { | 
| 392 | 141 |  |  |  |  | 13703 | $e->{address} = $1; | 
| 393 | 141 |  |  |  |  | 708 | $external = $1; | 
| 394 |  |  |  |  |  |  | } elsif (/^Advertising Router: $IP$/) { | 
| 395 | 141 |  |  |  |  | 14052 | $e->{routerid} = $1; | 
| 396 |  |  |  |  |  |  | } elsif (/^LS Seq Number: (0x$RE{num}{hex})$/) { | 
| 397 | 141 |  |  |  |  | 26738 | $e->{sequence} = $1; | 
| 398 |  |  |  |  |  |  | } elsif (/^Network Mask: $IP$/) { | 
| 399 | 138 |  |  |  |  | 26643 | $e->{netmask} = $1; | 
| 400 |  |  |  |  |  |  | } elsif (/^    Metric type: ([1-2])$/) { | 
| 401 | 138 |  |  |  |  | 26215 | $e->{type} = $1; | 
| 402 |  |  |  |  |  |  | } elsif (/^    Metric: $RE{num}{int}{-keep}$/) { | 
| 403 | 138 |  |  |  |  | 39026 | $e->{metric} = $1; | 
| 404 |  |  |  |  |  |  | } elsif (/^    Forwarding Address: $IP$/) { | 
| 405 | 138 |  |  |  |  | 39018 | $e->{forward} = $1; | 
| 406 |  |  |  |  |  |  | } elsif (! /^(Options|Checksum|Length|    External Route Tag):/) { | 
| 407 | 1 |  |  |  |  | 270 | die "$_ Unknown line at external $external.\n"; | 
| 408 |  |  |  |  |  |  | } | 
| 409 |  |  |  |  |  |  | } | 
| 410 | 4 | 100 |  |  |  | 19 | die "External $external not finished.\n" if $e; | 
| 411 | 3 |  |  |  |  | 17 | $self->{ospf}{database}{externals} = \@externals; | 
| 412 |  |  |  |  |  |  | } | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | sub parse_lsdb { | 
| 415 | 4 |  |  | 4 | 0 | 8 | my OSPF::LSDB::ospfd $self = shift; | 
| 416 | 4 |  |  |  |  | 17 | $self->parse_router(); | 
| 417 | 4 |  |  |  |  | 39 | $self->parse_network(); | 
| 418 | 4 |  |  |  |  | 20 | $self->parse_summary(); | 
| 419 | 4 |  |  |  |  | 18 | $self->parse_boundary(); | 
| 420 | 4 |  |  |  |  | 16 | $self->parse_external(); | 
| 421 |  |  |  |  |  |  | } | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | =pod | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | =over 4 | 
| 426 |  |  |  |  |  |  |  | 
| 427 |  |  |  |  |  |  | =item $self-Eparse(%files) | 
| 428 |  |  |  |  |  |  |  | 
| 429 |  |  |  |  |  |  | This function takes a hash with file names as value containing the | 
| 430 |  |  |  |  |  |  | B output data. | 
| 431 |  |  |  |  |  |  | The hash keys are named C, C, C, C, | 
| 432 |  |  |  |  |  |  | C, C. | 
| 433 |  |  |  |  |  |  | If a hash entry is missing, B is run instead to obtain the | 
| 434 |  |  |  |  |  |  | information dynamically. | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | The complete OSPF link state database is stored in the B field | 
| 437 |  |  |  |  |  |  | of the base class. | 
| 438 |  |  |  |  |  |  |  | 
| 439 |  |  |  |  |  |  | =back | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | =cut | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | sub parse { | 
| 444 | 4 |  |  | 4 | 1 | 12 | my OSPF::LSDB::ospfd $self = shift; | 
| 445 | 4 |  |  |  |  | 17 | my %files = @_; | 
| 446 | 4 |  |  |  |  | 27 | $self->read_files(%files); | 
| 447 | 4 |  |  |  |  | 29 | $self->parse_self(); | 
| 448 | 4 |  |  |  |  | 20 | $self->parse_lsdb(); | 
| 449 | 4 |  |  |  |  | 36 | $self->{ospf}{ipv6} = 0; | 
| 450 |  |  |  |  |  |  | } | 
| 451 |  |  |  |  |  |  |  | 
| 452 |  |  |  |  |  |  | =pod | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | This module has been tested with OpenBSD 4.8 and 5.1. | 
| 455 |  |  |  |  |  |  | If it works with other versions is unknown. | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | =head1 ERRORS | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | The methods die if any error occurs. | 
| 460 |  |  |  |  |  |  |  | 
| 461 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | L, | 
| 464 |  |  |  |  |  |  | L | 
| 465 |  |  |  |  |  |  |  | 
| 466 |  |  |  |  |  |  | L | 
| 467 |  |  |  |  |  |  |  | 
| 468 |  |  |  |  |  |  | =head1 AUTHORS | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | Alexander Bluhm | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | =cut | 
| 473 |  |  |  |  |  |  |  | 
| 474 |  |  |  |  |  |  | 1; |