| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
########################################################################## |
|
4
|
|
|
|
|
|
|
# Copyright (c) 2012-2022 Alexander Bluhm |
|
5
|
|
|
|
|
|
|
# |
|
6
|
|
|
|
|
|
|
# Permission to use, copy, modify, and distribute this software for any |
|
7
|
|
|
|
|
|
|
# purpose with or without fee is hereby granted, provided that the above |
|
8
|
|
|
|
|
|
|
# copyright notice and this permission notice appear in all copies. |
|
9
|
|
|
|
|
|
|
# |
|
10
|
|
|
|
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
11
|
|
|
|
|
|
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
12
|
|
|
|
|
|
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
13
|
|
|
|
|
|
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
14
|
|
|
|
|
|
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
15
|
|
|
|
|
|
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
16
|
|
|
|
|
|
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
17
|
|
|
|
|
|
|
########################################################################## |
|
18
|
|
|
|
|
|
|
|
|
19
|
1
|
|
|
1
|
|
4211855
|
use strict; |
|
|
1
|
|
|
|
|
10
|
|
|
|
1
|
|
|
|
|
61
|
|
|
20
|
1
|
|
|
1
|
|
11
|
use warnings; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
86
|
|
|
21
|
1
|
|
|
1
|
|
640
|
use File::Temp; |
|
|
1
|
|
|
|
|
14254
|
|
|
|
1
|
|
|
|
|
67
|
|
|
22
|
1
|
|
|
1
|
|
516
|
use Getopt::Long qw(:config posix_default bundling); |
|
|
1
|
|
|
|
|
7585
|
|
|
|
1
|
|
|
|
|
4
|
|
|
23
|
1
|
|
|
1
|
|
581
|
use IPC::Open2; |
|
|
1
|
|
|
|
|
2465
|
|
|
|
1
|
|
|
|
|
40
|
|
|
24
|
1
|
|
|
1
|
|
358
|
use POSIX; |
|
|
1
|
|
|
|
|
4930
|
|
|
|
1
|
|
|
|
|
4
|
|
|
25
|
1
|
|
|
1
|
|
2359
|
use Time::HiRes qw(time sleep); |
|
|
1
|
|
|
|
|
1147
|
|
|
|
1
|
|
|
|
|
3
|
|
|
26
|
1
|
|
|
1
|
|
518
|
use OSPF::LSDB::ospfd; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
29
|
|
|
27
|
1
|
|
|
1
|
|
377
|
use OSPF::LSDB::ospf6d; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
32
|
|
|
28
|
1
|
|
|
1
|
|
551
|
use OSPF::LSDB::View; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
29
|
|
|
29
|
1
|
|
|
1
|
|
499
|
use OSPF::LSDB::View6; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
28
|
|
|
30
|
1
|
|
|
1
|
|
358
|
use OSPF::LSDB::YAML; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
1148
|
|
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub usage(@) { |
|
33
|
1
|
50
|
|
1
|
|
37
|
print STDERR "Error: @_\n" if @_; |
|
34
|
1
|
|
|
|
|
49
|
print STDERR <
|
|
35
|
|
|
|
|
|
|
Periodically poll OSPF database from routing daemon and display it on X11. |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
Usage: $0 [-46bBcdDeEhlpPsSwWv] [-H user\@host] [-I interval] |
|
38
|
|
|
|
|
|
|
-4 disable IPv6 |
|
39
|
|
|
|
|
|
|
-6 enable IPv6 |
|
40
|
|
|
|
|
|
|
-b generate other area AS boundary router summary |
|
41
|
|
|
|
|
|
|
-B aggregate other area AS boundary router summary |
|
42
|
|
|
|
|
|
|
-c cluster identical networks |
|
43
|
|
|
|
|
|
|
-d show OSPF database diff between updates |
|
44
|
|
|
|
|
|
|
-D dump OSPF database after updates as YAML to stdout |
|
45
|
|
|
|
|
|
|
-e generate AS external networks |
|
46
|
|
|
|
|
|
|
-E aggregate AS external networks |
|
47
|
|
|
|
|
|
|
-h help, print usage |
|
48
|
|
|
|
|
|
|
-H user\@host use ssh to login into user\@host to run ospfctl there |
|
49
|
|
|
|
|
|
|
-I interval query interval in seconds, default 5 |
|
50
|
|
|
|
|
|
|
-l generate legend |
|
51
|
|
|
|
|
|
|
-p generate link and intra-area-prefix |
|
52
|
|
|
|
|
|
|
-P generate intra-area-prefix |
|
53
|
|
|
|
|
|
|
-s generate other area network summary |
|
54
|
|
|
|
|
|
|
-S aggregate other area network summary |
|
55
|
|
|
|
|
|
|
-w show most serious warning in dot graph |
|
56
|
|
|
|
|
|
|
-W show all warnings and areas in dot graph |
|
57
|
|
|
|
|
|
|
-v be verbose, print warnings to stdout |
|
58
|
|
|
|
|
|
|
EOF |
|
59
|
1
|
|
|
|
|
150
|
exit(2); |
|
60
|
|
|
|
|
|
|
} |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub main() { |
|
63
|
1
|
|
|
1
|
|
2
|
my $diff; |
|
64
|
|
|
|
|
|
|
my $dump; |
|
65
|
1
|
|
|
|
|
2
|
my $interval = 5; |
|
66
|
1
|
|
|
|
|
3
|
my $ipv6; |
|
67
|
|
|
|
|
|
|
my $legend; |
|
68
|
1
|
|
|
|
|
0
|
my $ssh; |
|
69
|
1
|
|
|
|
|
0
|
my %todo; |
|
70
|
|
|
|
|
|
|
GetOptions( |
|
71
|
0
|
|
|
0
|
|
0
|
'4' => sub { $ipv6 = 0 }, |
|
72
|
0
|
|
|
0
|
|
0
|
'6' => sub { $ipv6 = 1 }, |
|
73
|
0
|
|
|
0
|
|
0
|
'b' => sub { $todo{boundary}{generate} = 1 }, |
|
74
|
0
|
|
|
0
|
|
0
|
'B' => sub { $todo{boundary}{aggregate} = 1 }, |
|
75
|
0
|
|
|
0
|
|
0
|
'c' => sub { $todo{cluster} = 1 }, |
|
76
|
|
|
|
|
|
|
'd' => \$diff, |
|
77
|
|
|
|
|
|
|
'D' => \$dump, |
|
78
|
0
|
|
|
0
|
|
0
|
'e' => sub { $todo{external}{generate} = 1 }, |
|
79
|
0
|
|
|
0
|
|
0
|
'E' => sub { $todo{external}{aggregate} = 1 }, |
|
80
|
1
|
|
|
1
|
|
920
|
'h' => sub { usage() }, |
|
81
|
|
|
|
|
|
|
'H=s' => \$ssh, |
|
82
|
|
|
|
|
|
|
'I=i' => \$interval, |
|
83
|
|
|
|
|
|
|
'l' => \$legend, |
|
84
|
0
|
|
|
0
|
|
|
'p' => sub { $todo{prefix}{generate} = 1 }, |
|
85
|
0
|
|
|
0
|
|
|
'P' => sub { $todo{prefix}{aggregate} = 1 }, |
|
86
|
0
|
|
|
0
|
|
|
's' => sub { $todo{summary}{generate} = 1 }, |
|
87
|
0
|
|
|
0
|
|
|
'S' => sub { $todo{summary}{aggregate} = 1 }, |
|
88
|
0
|
|
|
0
|
|
|
'w' => sub { $todo{warning}{single} = 1 }, |
|
89
|
0
|
|
|
0
|
|
|
'W' => sub { $todo{warning}{all} = 1 }, |
|
90
|
0
|
|
|
0
|
|
|
'v' => sub { $todo{verbose} = 1 }, |
|
91
|
1
|
0
|
|
|
|
15
|
) or usage("Bad option"); |
|
92
|
0
|
0
|
|
|
|
|
usage("No arguments allowed") if @ARGV > 0; |
|
93
|
|
|
|
|
|
|
|
|
94
|
0
|
|
|
|
|
|
foreach my $option (qw(boundary external prefix summary warning)) { |
|
95
|
0
|
0
|
|
|
|
|
if (keys %{$todo{$option} || {}} > 1) { |
|
|
0
|
0
|
|
|
|
|
|
|
96
|
0
|
|
|
|
|
|
my $opt = substr($option, 0, 1); |
|
97
|
0
|
|
|
|
|
|
usage("Options -$opt and -".uc($opt)." used together"); |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
} |
|
100
|
|
|
|
|
|
|
|
|
101
|
0
|
0
|
|
|
|
|
if ($todo{prefix}) { |
|
102
|
0
|
|
|
|
|
|
$todo{intra}{generate} = 1; |
|
103
|
0
|
0
|
|
|
|
|
$todo{link}{generate} = 1 if $todo{prefix}{generate}; |
|
104
|
|
|
|
|
|
|
} |
|
105
|
|
|
|
|
|
|
|
|
106
|
0
|
|
|
|
|
|
my @cmd = qw(dot -Txlib); |
|
107
|
0
|
|
|
|
|
|
my($pid, $fh, $gone, $term); |
|
108
|
0
|
|
|
|
|
|
$term = 0; |
|
109
|
|
|
|
|
|
|
$SIG{INT} = sub { |
|
110
|
0
|
|
|
0
|
|
|
local $!; |
|
111
|
0
|
0
|
|
|
|
|
kill SIGTERM, $pid if $pid; |
|
112
|
0
|
|
|
|
|
|
$SIG{'INT'} = 'DEFAULT'; |
|
113
|
0
|
|
|
|
|
|
$term = 1; |
|
114
|
0
|
|
|
|
|
|
kill SIGINT, $$; |
|
115
|
0
|
|
|
|
|
|
}; |
|
116
|
|
|
|
|
|
|
$SIG{CHLD} = sub { |
|
117
|
0
|
0
|
|
0
|
|
|
if ($pid) { |
|
118
|
0
|
|
|
|
|
|
local ($!, $?); |
|
119
|
0
|
0
|
|
|
|
|
if (waitpid($pid, POSIX::WNOHANG) > 0) { |
|
120
|
0
|
0
|
0
|
|
|
|
die "'@cmd' failed: $?" if $? && |
|
|
|
|
0
|
|
|
|
|
|
121
|
|
|
|
|
|
|
((WIFEXITED($?) && WEXITSTATUS($?) != 0) || |
|
122
|
|
|
|
|
|
|
(WIFSIGNALED($?) && WTERMSIG($?) != SIGTERM)); |
|
123
|
0
|
|
|
|
|
|
undef $pid; |
|
124
|
0
|
|
|
|
|
|
$gone = 1; |
|
125
|
|
|
|
|
|
|
} |
|
126
|
|
|
|
|
|
|
} |
|
127
|
0
|
|
|
|
|
|
}; |
|
128
|
|
|
|
|
|
|
|
|
129
|
0
|
0
|
|
|
|
|
my $class = $ipv6 ? 'OSPF::LSDB::View6' : 'OSPF::LSDB::View'; |
|
130
|
0
|
0
|
|
|
|
|
if ($legend) { |
|
131
|
0
|
|
|
|
|
|
my $dot = $class->legend(); |
|
132
|
0
|
|
|
|
|
|
$gone = 0; |
|
133
|
0
|
0
|
|
|
|
|
$pid = open2(undef, $fh, @cmd) |
|
134
|
|
|
|
|
|
|
or die "Open pipe to '@cmd' failed: $!"; |
|
135
|
0
|
|
|
|
|
|
print $fh $dot, "\n"; |
|
136
|
0
|
0
|
|
|
|
|
close($fh) |
|
137
|
|
|
|
|
|
|
or die "Close pipe to '@cmd' failed: $!"; |
|
138
|
0
|
|
|
|
|
|
pause(); |
|
139
|
0
|
|
|
|
|
|
exit 0; |
|
140
|
|
|
|
|
|
|
} |
|
141
|
|
|
|
|
|
|
|
|
142
|
0
|
|
|
|
|
|
my($oldtime, $oldyaml); |
|
143
|
0
|
|
|
|
|
|
for (;;) { |
|
144
|
0
|
|
|
|
|
|
my $time = time(); |
|
145
|
0
|
0
|
|
|
|
|
if ($oldtime) { |
|
146
|
0
|
|
|
|
|
|
my $sleeptime = $interval - ($time - $oldtime); |
|
147
|
0
|
0
|
|
|
|
|
if ($sleeptime > 0) { |
|
148
|
0
|
|
|
|
|
|
select(undef, undef, undef, $sleeptime); |
|
149
|
|
|
|
|
|
|
} |
|
150
|
|
|
|
|
|
|
} |
|
151
|
0
|
|
|
|
|
|
$oldtime = time(); |
|
152
|
|
|
|
|
|
|
|
|
153
|
0
|
0
|
|
|
|
|
my $ospfclass = $ipv6 ? 'OSPF::LSDB::ospf6d' : 'OSPF::LSDB::ospfd'; |
|
154
|
0
|
|
|
|
|
|
my $ospf = $ospfclass->new(ssh => $ssh); |
|
155
|
0
|
|
|
|
|
|
eval { $ospf->parse(); }; |
|
|
0
|
|
|
|
|
|
|
|
156
|
0
|
0
|
|
|
|
|
if ($@) { |
|
157
|
0
|
|
|
|
|
|
warn $@; |
|
158
|
0
|
0
|
|
|
|
|
kill SIGTERM, $pid if $pid; |
|
159
|
0
|
|
|
|
|
|
next; |
|
160
|
|
|
|
|
|
|
} |
|
161
|
|
|
|
|
|
|
|
|
162
|
0
|
|
|
|
|
|
my $yamlospf = OSPF::LSDB::YAML->new($ospf); |
|
163
|
0
|
0
|
0
|
|
|
|
if (defined $ipv6 && $ipv6 != $yamlospf->ipv6()) { |
|
164
|
0
|
|
|
|
|
|
die "Address family does not match -4 and -6 options.\n"; |
|
165
|
|
|
|
|
|
|
} |
|
166
|
0
|
|
|
|
|
|
my $yaml = $yamlospf->Dump(); |
|
167
|
0
|
|
|
|
|
|
$yaml =~ s/^\s+(age|sequence): .*$//mg; |
|
168
|
0
|
0
|
0
|
|
|
|
next if ($oldyaml && $oldyaml eq $yaml && ! $gone) || $term; |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
169
|
0
|
0
|
|
|
|
|
if ($dump) { |
|
170
|
0
|
|
|
|
|
|
print $yaml; |
|
171
|
|
|
|
|
|
|
} |
|
172
|
0
|
0
|
0
|
|
|
|
if ($diff && $oldyaml) { |
|
173
|
0
|
|
|
|
|
|
my %args = ( |
|
174
|
|
|
|
|
|
|
SUFFIX => ".yaml", |
|
175
|
|
|
|
|
|
|
TEMPLATE => "ospfview-XXXXXXXXXX", |
|
176
|
|
|
|
|
|
|
TMPDIR => 1, |
|
177
|
|
|
|
|
|
|
UNLINK => 1 |
|
178
|
|
|
|
|
|
|
); |
|
179
|
0
|
|
|
|
|
|
my $old = File::Temp->new(%args); |
|
180
|
0
|
|
|
|
|
|
print $old $oldyaml; |
|
181
|
0
|
|
|
|
|
|
my $new = File::Temp->new(%args); |
|
182
|
0
|
|
|
|
|
|
print $new $yaml; |
|
183
|
0
|
|
|
|
|
|
system('diff', '-up', $old->filename, $new->filename); |
|
184
|
|
|
|
|
|
|
} |
|
185
|
0
|
|
|
|
|
|
$oldyaml = $yaml; |
|
186
|
|
|
|
|
|
|
|
|
187
|
0
|
|
|
|
|
|
my $view = $class->new($ospf); |
|
188
|
0
|
|
|
|
|
|
my $dot = $view->graph(%todo); |
|
189
|
0
|
0
|
|
|
|
|
if ($todo{verbose}) { |
|
190
|
0
|
|
|
|
|
|
my @errors = $view->get_errors; |
|
191
|
0
|
0
|
|
|
|
|
print map { "$_\n" } @errors, "" if @errors; |
|
|
0
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
|
|
194
|
0
|
|
|
|
|
|
my $chldsigset = POSIX::SigSet->new( &POSIX::SIGCHLD ); |
|
195
|
0
|
|
|
|
|
|
my $oldsigset = POSIX::SigSet->new(); |
|
196
|
0
|
0
|
|
|
|
|
sigprocmask(POSIX::SIG_BLOCK, $chldsigset, $oldsigset) |
|
197
|
|
|
|
|
|
|
or die "Block sigprocmask failed: $!"; |
|
198
|
0
|
0
|
|
|
|
|
if ($pid) { |
|
199
|
0
|
|
|
|
|
|
kill SIGTERM, $pid; |
|
200
|
0
|
0
|
|
|
|
|
if (waitpid($pid, 0) > 0) { |
|
201
|
0
|
0
|
0
|
|
|
|
die "'@cmd' failed: $?" if $? && |
|
|
|
|
0
|
|
|
|
|
|
202
|
|
|
|
|
|
|
((WIFEXITED($?) && WEXITSTATUS($?) != 0) || |
|
203
|
|
|
|
|
|
|
(WIFSIGNALED($?) && WTERMSIG($?) != SIGTERM)); |
|
204
|
0
|
|
|
|
|
|
undef $pid; |
|
205
|
0
|
|
|
|
|
|
$gone = 1; |
|
206
|
|
|
|
|
|
|
} |
|
207
|
|
|
|
|
|
|
} |
|
208
|
0
|
0
|
|
|
|
|
sigprocmask(POSIX::SIG_SETMASK, $oldsigset, undef) |
|
209
|
|
|
|
|
|
|
or die "Setmask sigprocmask failed: $!"; |
|
210
|
|
|
|
|
|
|
|
|
211
|
0
|
|
|
|
|
|
$gone = 0; |
|
212
|
0
|
0
|
|
|
|
|
$pid = open2(undef, $fh, @cmd) |
|
213
|
|
|
|
|
|
|
or die "Open pipe to '@cmd' failed: $!"; |
|
214
|
0
|
|
|
|
|
|
print $fh $dot, "\n"; |
|
215
|
0
|
0
|
|
|
|
|
close($fh) |
|
216
|
|
|
|
|
|
|
or die "Close pipe to '@cmd' failed: $!"; |
|
217
|
|
|
|
|
|
|
} |
|
218
|
|
|
|
|
|
|
} |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
main(); |