| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
### |
|
2
|
|
|
|
|
|
|
# Copyright 1998, 1999 Massachusetts Institute of Technology |
|
3
|
|
|
|
|
|
|
# Copyright 2000-2005 Daniel Hagerty |
|
4
|
|
|
|
|
|
|
# |
|
5
|
|
|
|
|
|
|
# Permission to use, copy, modify, distribute, and sell this software and its |
|
6
|
|
|
|
|
|
|
# documentation for any purpose is hereby granted without fee, provided that |
|
7
|
|
|
|
|
|
|
# the above copyright notice appear in all copies and that both that |
|
8
|
|
|
|
|
|
|
# copyright notice and this permission notice appear in supporting |
|
9
|
|
|
|
|
|
|
# documentation, and that the name of M.I.T. not be used in advertising or |
|
10
|
|
|
|
|
|
|
# publicity pertaining to distribution of the software without specific, |
|
11
|
|
|
|
|
|
|
# written prior permission. M.I.T. makes no representations about the |
|
12
|
|
|
|
|
|
|
# suitability of this software for any purpose. It is provided "as is" |
|
13
|
|
|
|
|
|
|
# without express or implied warranty. |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
### |
|
16
|
|
|
|
|
|
|
# File: traceroute.pm |
|
17
|
|
|
|
|
|
|
# Author: Daniel Hagerty, hag@ai.mit.edu |
|
18
|
|
|
|
|
|
|
# Date: Tue Mar 17 13:44:00 1998 |
|
19
|
|
|
|
|
|
|
# Description: Perl traceroute module for performing traceroute(1) |
|
20
|
|
|
|
|
|
|
# functionality. |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# Currently attempts to parse the output of the system traceroute command, |
|
23
|
|
|
|
|
|
|
# which it expects will behave like the standard LBL traceroute program. |
|
24
|
|
|
|
|
|
|
# If it doesn't, (Windows, HPUX come to mind) you lose. |
|
25
|
|
|
|
|
|
|
# |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# Could eventually be broken into several classes that know how to |
|
28
|
|
|
|
|
|
|
# deal with various traceroutes; could attempt to auto-recognize the |
|
29
|
|
|
|
|
|
|
# particular traceroute and parse it. |
|
30
|
|
|
|
|
|
|
# |
|
31
|
|
|
|
|
|
|
# Has a couple of random useful hooks for child classes to override. |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
package Net::Traceroute; |
|
34
|
|
|
|
|
|
|
|
|
35
|
22
|
|
|
22
|
|
692406
|
use strict; |
|
|
22
|
|
|
|
|
86
|
|
|
|
22
|
|
|
|
|
963
|
|
|
36
|
22
|
|
|
22
|
|
114
|
no strict qw(subs); |
|
|
22
|
|
|
|
|
45
|
|
|
|
22
|
|
|
|
|
764
|
|
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
#require 5.xxx; # We'll probably need this |
|
39
|
|
|
|
|
|
|
|
|
40
|
22
|
|
|
22
|
|
125
|
use vars qw(@EXPORT $VERSION @ISA); |
|
|
22
|
|
|
|
|
38
|
|
|
|
22
|
|
|
|
|
1836
|
|
|
41
|
|
|
|
|
|
|
|
|
42
|
22
|
|
|
22
|
|
187
|
use Exporter; |
|
|
22
|
|
|
|
|
46
|
|
|
|
22
|
|
|
|
|
1019
|
|
|
43
|
22
|
|
|
22
|
|
24514
|
use IO::Pipe; |
|
|
22
|
|
|
|
|
272605
|
|
|
|
22
|
|
|
|
|
731
|
|
|
44
|
22
|
|
|
22
|
|
25685
|
use IO::Select; |
|
|
22
|
|
|
|
|
49101
|
|
|
|
22
|
|
|
|
|
1756
|
|
|
45
|
22
|
|
|
22
|
|
23633
|
use Socket; |
|
|
22
|
|
|
|
|
97939
|
|
|
|
22
|
|
|
|
|
15395
|
|
|
46
|
22
|
|
|
22
|
|
214
|
use Symbol qw(qualify_to_ref); |
|
|
22
|
|
|
|
|
49
|
|
|
|
22
|
|
|
|
|
1675
|
|
|
47
|
22
|
|
|
22
|
|
43895
|
use Time::HiRes qw(time); |
|
|
22
|
|
|
|
|
51330
|
|
|
|
22
|
|
|
|
|
132
|
|
|
48
|
22
|
|
|
22
|
|
26270
|
use Errno qw(EAGAIN EINTR); |
|
|
22
|
|
|
|
|
39939
|
|
|
|
22
|
|
|
|
|
3578
|
|
|
49
|
22
|
|
|
22
|
|
36982
|
use Data::Dumper; # Debugging |
|
|
22
|
|
|
|
|
272260
|
|
|
|
22
|
|
|
|
|
8014
|
|
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
$VERSION = "1.15"; # Version number is only incremented by |
|
52
|
|
|
|
|
|
|
# hand. |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
@EXPORT = qw( |
|
57
|
|
|
|
|
|
|
TRACEROUTE_OK |
|
58
|
|
|
|
|
|
|
TRACEROUTE_TIMEOUT |
|
59
|
|
|
|
|
|
|
TRACEROUTE_UNKNOWN |
|
60
|
|
|
|
|
|
|
TRACEROUTE_BSDBUG |
|
61
|
|
|
|
|
|
|
TRACEROUTE_UNREACH_NET |
|
62
|
|
|
|
|
|
|
TRACEROUTE_UNREACH_HOST |
|
63
|
|
|
|
|
|
|
TRACEROUTE_UNREACH_PROTO |
|
64
|
|
|
|
|
|
|
TRACEROUTE_UNREACH_NEEDFRAG |
|
65
|
|
|
|
|
|
|
TRACEROUTE_UNREACH_SRCFAIL |
|
66
|
|
|
|
|
|
|
TRACEROUTE_UNREACH_FILTER_PROHIB |
|
67
|
|
|
|
|
|
|
TRACEROUTE_UNREACH_ADDR |
|
68
|
|
|
|
|
|
|
TRACEROUTE_UNREACH_PORT |
|
69
|
|
|
|
|
|
|
TRACEROUTE_SOURCE_QUENCH |
|
70
|
|
|
|
|
|
|
TRACEROUTE_INTERRUPTED |
|
71
|
|
|
|
|
|
|
); |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
### |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
## Exported functions. |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# Perl's facist mode gets very grumbly if a few things aren't declared |
|
78
|
|
|
|
|
|
|
# first. |
|
79
|
|
|
|
|
|
|
|
|
80
|
505
|
|
|
505
|
1
|
1714
|
sub TRACEROUTE_OK { 0 } |
|
81
|
66
|
|
|
66
|
1
|
387
|
sub TRACEROUTE_TIMEOUT { 1 } |
|
82
|
36
|
|
|
36
|
1
|
238
|
sub TRACEROUTE_UNKNOWN { 2 } |
|
83
|
22
|
|
|
22
|
1
|
138
|
sub TRACEROUTE_BSDBUG { 3 } |
|
84
|
69
|
|
|
69
|
1
|
274
|
sub TRACEROUTE_UNREACH_NET { 4 } |
|
85
|
44
|
|
|
44
|
1
|
120
|
sub TRACEROUTE_UNREACH_HOST { 5 } |
|
86
|
44
|
|
|
44
|
1
|
139
|
sub TRACEROUTE_UNREACH_PROTO { 6 } |
|
87
|
22
|
|
|
22
|
1
|
74
|
sub TRACEROUTE_UNREACH_NEEDFRAG { 7 } |
|
88
|
22
|
|
|
22
|
1
|
73
|
sub TRACEROUTE_UNREACH_SRCFAIL { 8 } |
|
89
|
72
|
|
|
72
|
1
|
374
|
sub TRACEROUTE_UNREACH_FILTER_PROHIB { 9 } |
|
90
|
22
|
|
|
22
|
1
|
68
|
sub TRACEROUTE_UNREACH_ADDR { 10 } |
|
91
|
44
|
|
|
44
|
1
|
120
|
sub TRACEROUTE_UNREACH_PORT { 11 } |
|
92
|
22
|
|
|
22
|
1
|
63
|
sub TRACEROUTE_SOURCE_QUENCH { 12 } |
|
93
|
22
|
|
|
22
|
1
|
56
|
sub TRACEROUTE_INTERRUPTED { 13 } |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
## Internal data used throughout the module |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# Instance variables that are nothing special, and have an obvious |
|
98
|
|
|
|
|
|
|
# corresponding accessor/mutator method. |
|
99
|
|
|
|
|
|
|
my @public_instance_vars = |
|
100
|
|
|
|
|
|
|
qw( |
|
101
|
|
|
|
|
|
|
base_port |
|
102
|
|
|
|
|
|
|
debug |
|
103
|
|
|
|
|
|
|
host |
|
104
|
|
|
|
|
|
|
max_ttl |
|
105
|
|
|
|
|
|
|
packetlen |
|
106
|
|
|
|
|
|
|
queries |
|
107
|
|
|
|
|
|
|
query_timeout |
|
108
|
|
|
|
|
|
|
source_address |
|
109
|
|
|
|
|
|
|
text |
|
110
|
|
|
|
|
|
|
trace_program |
|
111
|
|
|
|
|
|
|
timeout |
|
112
|
|
|
|
|
|
|
no_fragment |
|
113
|
|
|
|
|
|
|
use_icmp |
|
114
|
|
|
|
|
|
|
use_tcp |
|
115
|
|
|
|
|
|
|
tos |
|
116
|
|
|
|
|
|
|
); |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
my @simple_instance_vars = ( |
|
119
|
|
|
|
|
|
|
qw( |
|
120
|
|
|
|
|
|
|
pathmtu |
|
121
|
|
|
|
|
|
|
stat |
|
122
|
|
|
|
|
|
|
), |
|
123
|
|
|
|
|
|
|
@public_instance_vars, |
|
124
|
|
|
|
|
|
|
); |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
# Field offsets for query info array |
|
127
|
22
|
|
|
22
|
|
206
|
use constant query_stat_offset => 0; |
|
|
22
|
|
|
|
|
43
|
|
|
|
22
|
|
|
|
|
1540
|
|
|
128
|
22
|
|
|
22
|
|
270
|
use constant query_host_offset => 1; |
|
|
22
|
|
|
|
|
78
|
|
|
|
22
|
|
|
|
|
1086
|
|
|
129
|
22
|
|
|
22
|
|
214
|
use constant query_time_offset => 2; |
|
|
22
|
|
|
|
|
38
|
|
|
|
22
|
|
|
|
|
891
|
|
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# We keep track of the most recently seen chunk of the traceroute for |
|
132
|
|
|
|
|
|
|
# parsing purposes. |
|
133
|
22
|
|
|
22
|
|
107
|
use constant token_addr => 0; |
|
|
22
|
|
|
|
|
44
|
|
|
|
22
|
|
|
|
|
853
|
|
|
134
|
22
|
|
|
22
|
|
198
|
use constant token_time => 1; |
|
|
22
|
|
|
|
|
62
|
|
|
|
22
|
|
|
|
|
2252
|
|
|
135
|
22
|
|
|
22
|
|
1345
|
use constant token_flag => 2; |
|
|
22
|
|
|
|
|
1352
|
|
|
|
22
|
|
|
|
|
116684
|
|
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
# Map ! notation traceroute uses for various icmp packet types |
|
138
|
|
|
|
|
|
|
# it may receive. |
|
139
|
|
|
|
|
|
|
my %icmp_map_v4 = ( |
|
140
|
|
|
|
|
|
|
N => TRACEROUTE_UNREACH_NET, |
|
141
|
|
|
|
|
|
|
H => TRACEROUTE_UNREACH_HOST, |
|
142
|
|
|
|
|
|
|
P => TRACEROUTE_UNREACH_PROTO, |
|
143
|
|
|
|
|
|
|
F => TRACEROUTE_UNREACH_NEEDFRAG, |
|
144
|
|
|
|
|
|
|
S => TRACEROUTE_UNREACH_SRCFAIL, |
|
145
|
|
|
|
|
|
|
X => TRACEROUTE_UNREACH_FILTER_PROHIB, |
|
146
|
|
|
|
|
|
|
'!' => TRACEROUTE_BSDBUG, |
|
147
|
|
|
|
|
|
|
); |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
my %icmp_map_v6 = ( |
|
150
|
|
|
|
|
|
|
N => TRACEROUTE_UNREACH_NET, |
|
151
|
|
|
|
|
|
|
P => TRACEROUTE_UNREACH_FILTER_PROHIB, |
|
152
|
|
|
|
|
|
|
# Unlikely to be seen in the wild: |
|
153
|
|
|
|
|
|
|
# S => unreach notneighbor, |
|
154
|
|
|
|
|
|
|
A => TRACEROUTE_UNREACH_ADDR, |
|
155
|
|
|
|
|
|
|
'!' => TRACEROUTE_UNREACH_PORT, |
|
156
|
|
|
|
|
|
|
); |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# Entries Q, I, T, and U have never been tested. For the most part, I |
|
159
|
|
|
|
|
|
|
# don't know how to produce them or they're so rare I couldn't be |
|
160
|
|
|
|
|
|
|
# bothered. |
|
161
|
|
|
|
|
|
|
my %icmp_map_cisco = ( |
|
162
|
|
|
|
|
|
|
A => TRACEROUTE_UNREACH_FILTER_PROHIB, |
|
163
|
|
|
|
|
|
|
Q => TRACEROUTE_SOURCE_QUENCH, |
|
164
|
|
|
|
|
|
|
I => TRACEROUTE_INTERRUPTED, |
|
165
|
|
|
|
|
|
|
U => TRACEROUTE_UNREACH_PORT, |
|
166
|
|
|
|
|
|
|
H => TRACEROUTE_UNREACH_HOST, |
|
167
|
|
|
|
|
|
|
N => TRACEROUTE_UNREACH_NET, |
|
168
|
|
|
|
|
|
|
P => TRACEROUTE_UNREACH_PROTO, |
|
169
|
|
|
|
|
|
|
T => TRACEROUTE_TIMEOUT, |
|
170
|
|
|
|
|
|
|
# Handled elsehow: |
|
171
|
|
|
|
|
|
|
# ? => unknown packet type, |
|
172
|
|
|
|
|
|
|
); |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
### |
|
175
|
|
|
|
|
|
|
# Public methods |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
# Constructor |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub new ($;%) { |
|
180
|
24
|
|
|
24
|
0
|
30182
|
my $self = shift; |
|
181
|
24
|
|
66
|
|
|
224
|
my $type = ref($self) || $self; |
|
182
|
|
|
|
|
|
|
|
|
183
|
24
|
|
|
|
|
96
|
my %arg = @_; |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# We implement a goofy UI so that all programmers can use |
|
186
|
|
|
|
|
|
|
# Net::Traceroute as a constructor for all types of object. |
|
187
|
24
|
100
|
|
|
|
117
|
if(exists($arg{backend})) { |
|
188
|
3
|
|
|
|
|
5
|
my $backend = $arg{backend}; |
|
189
|
3
|
100
|
|
|
|
13
|
if($backend ne "Parser") { |
|
190
|
2
|
|
|
|
|
5
|
my $module = "Net::Traceroute::$backend"; |
|
191
|
2
|
|
|
|
|
124
|
eval "require $module"; |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
# Ignore error on the possibility that they just defined |
|
194
|
|
|
|
|
|
|
# the module at runtime, rather than an actual module in |
|
195
|
|
|
|
|
|
|
# the filesystem. |
|
196
|
2
|
|
|
|
|
18
|
my $newref = qualify_to_ref("new", $module); |
|
197
|
2
|
|
|
|
|
49
|
my $newcode = *{$newref}{CODE}; |
|
|
2
|
|
|
|
|
5
|
|
|
198
|
2
|
100
|
|
|
|
8
|
if(!defined($newcode)) { |
|
199
|
1
|
|
|
|
|
9
|
die "Backend implementation $backend has no new"; |
|
200
|
|
|
|
|
|
|
} |
|
201
|
1
|
|
|
|
|
3
|
return(&{$newcode}($module, @_)); |
|
|
1
|
|
|
|
|
5
|
|
|
202
|
|
|
|
|
|
|
} |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
22
|
100
|
|
|
|
130
|
if(!ref($self)) { |
|
206
|
21
|
|
|
|
|
297
|
$self = bless {}, $type; |
|
207
|
|
|
|
|
|
|
} |
|
208
|
|
|
|
|
|
|
|
|
209
|
22
|
|
|
|
|
148
|
$self->init(%arg); |
|
210
|
22
|
|
|
|
|
321
|
$self; |
|
211
|
|
|
|
|
|
|
} |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub init { |
|
214
|
22
|
|
|
22
|
0
|
51
|
my $self = shift; |
|
215
|
22
|
|
|
|
|
53
|
my %arg = @_; |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
# Take our constructer arguments and initialize the attributes with |
|
218
|
|
|
|
|
|
|
# them. |
|
219
|
22
|
|
|
|
|
40
|
my $var; |
|
220
|
22
|
|
|
|
|
70
|
foreach $var (@public_instance_vars) { |
|
221
|
330
|
100
|
|
|
|
696
|
if(defined($arg{$var})) { |
|
222
|
6
|
|
|
|
|
27
|
$self->$var($arg{$var}); |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
} |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# Initialize debug if it isn't already. |
|
227
|
22
|
50
|
|
|
|
122
|
$self->debug(0) if(!defined($self->debug)); |
|
228
|
22
|
100
|
|
|
|
98
|
$self->trace_program("traceroute") if(!defined($self->trace_program)); |
|
229
|
|
|
|
|
|
|
|
|
230
|
22
|
|
|
|
|
125
|
$self->debug_print(1, "Running in debug mode\n"); |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
# Initialize status |
|
233
|
22
|
|
|
|
|
87
|
$self->stat(TRACEROUTE_UNKNOWN); |
|
234
|
|
|
|
|
|
|
|
|
235
|
22
|
100
|
|
|
|
92
|
if(defined($self->host)) { |
|
|
|
50
|
|
|
|
|
|
|
236
|
2
|
|
|
|
|
8
|
$self->traceroute; |
|
237
|
|
|
|
|
|
|
} elsif(defined($self->text)) { |
|
238
|
0
|
|
|
|
|
0
|
$self->_parse($self->text) |
|
239
|
|
|
|
|
|
|
} |
|
240
|
|
|
|
|
|
|
|
|
241
|
22
|
|
|
|
|
172
|
$self->debug_print(9, Dumper($self)); |
|
242
|
|
|
|
|
|
|
} |
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
sub clone ($;%) { |
|
245
|
1
|
|
|
1
|
0
|
2
|
my $self = shift; |
|
246
|
1
|
|
|
|
|
17
|
my $type = ref($self); |
|
247
|
|
|
|
|
|
|
|
|
248
|
1
|
|
|
|
|
4
|
my %arg = @_; |
|
249
|
|
|
|
|
|
|
|
|
250
|
1
|
50
|
|
|
|
4
|
die "Can't clone a non-object!" unless($type); |
|
251
|
|
|
|
|
|
|
|
|
252
|
1
|
|
|
|
|
4
|
my $clone = bless {}, $type; |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# Does a shallow copy of the hash key/values to the new hash. |
|
255
|
1
|
50
|
|
|
|
6
|
if(ref($self)) { |
|
256
|
1
|
|
|
|
|
1
|
my($key, $val); |
|
257
|
1
|
|
|
|
|
3
|
while(($key, $val) = each %{$self}) { |
|
|
5
|
|
|
|
|
17
|
|
|
258
|
4
|
|
|
|
|
9
|
$clone->{$key} = $val; |
|
259
|
|
|
|
|
|
|
} |
|
260
|
|
|
|
|
|
|
} |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
# Take our constructer arguments and initialize the attributes with |
|
263
|
|
|
|
|
|
|
# them. |
|
264
|
1
|
|
|
|
|
3
|
my $var; |
|
265
|
1
|
|
|
|
|
3
|
foreach $var (@public_instance_vars) { |
|
266
|
15
|
100
|
|
|
|
32
|
if(defined($arg{$var})) { |
|
267
|
1
|
|
|
|
|
5
|
$clone->$var($arg{$var}); |
|
268
|
|
|
|
|
|
|
} |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# Initialize status |
|
272
|
1
|
|
|
|
|
5
|
$clone->stat(TRACEROUTE_UNKNOWN); |
|
273
|
|
|
|
|
|
|
|
|
274
|
1
|
50
|
|
|
|
4
|
if(defined($clone->host)) { |
|
|
|
50
|
|
|
|
|
|
|
275
|
0
|
|
|
|
|
0
|
$clone->traceroute; |
|
276
|
|
|
|
|
|
|
} elsif(defined($clone->text)) { |
|
277
|
0
|
|
|
|
|
0
|
$clone->_parse($clone->text) |
|
278
|
|
|
|
|
|
|
} |
|
279
|
|
|
|
|
|
|
|
|
280
|
1
|
|
|
|
|
5
|
$clone->debug_print(9, Dumper($clone)); |
|
281
|
|
|
|
|
|
|
|
|
282
|
1
|
|
|
|
|
11
|
return($clone); |
|
283
|
|
|
|
|
|
|
} |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
## |
|
286
|
|
|
|
|
|
|
# Methods |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# Do the actual work. Not really a published interface; completely |
|
289
|
|
|
|
|
|
|
# useable from the constructor. |
|
290
|
|
|
|
|
|
|
sub traceroute ($) { |
|
291
|
2
|
|
|
2
|
1
|
4
|
my $self = shift; |
|
292
|
2
|
|
|
|
|
6
|
my $host = $self->host(); |
|
293
|
|
|
|
|
|
|
|
|
294
|
2
|
|
|
|
|
14
|
$self->debug_print(1, "Performing traceroute\n"); |
|
295
|
|
|
|
|
|
|
|
|
296
|
2
|
50
|
|
|
|
8
|
die "No host provided!" unless $host; |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
# Sit in a select loop on the incoming text from traceroute, |
|
299
|
|
|
|
|
|
|
# waiting for a timeout if we need to. Accumulate the text for |
|
300
|
|
|
|
|
|
|
# parsing later in one fell swoop. |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
# Note time. Time::HiRes will give us floating point. |
|
303
|
2
|
|
|
|
|
5
|
my $start_time; |
|
304
|
|
|
|
|
|
|
my $end_time; |
|
305
|
2
|
|
|
|
|
14
|
my $total_wait = $self->timeout(); |
|
306
|
2
|
|
|
|
|
4
|
my @this_wait; |
|
307
|
2
|
100
|
|
|
|
7
|
if(defined($total_wait)) { |
|
308
|
1
|
|
|
|
|
6
|
$start_time = time(); |
|
309
|
1
|
|
|
|
|
10
|
push(@this_wait, $total_wait); |
|
310
|
1
|
|
|
|
|
4
|
$end_time = $start_time + $total_wait; |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
|
|
313
|
2
|
|
|
|
|
7
|
my $tr_pipe = $self->_make_pipe(); |
|
314
|
2
|
|
|
|
|
96
|
my $select = new IO::Select($tr_pipe); |
|
315
|
|
|
|
|
|
|
|
|
316
|
2
|
|
|
|
|
397
|
$self->_zero_text_accumulator(); |
|
317
|
2
|
|
|
|
|
38
|
$self->_zero_hops(); |
|
318
|
|
|
|
|
|
|
|
|
319
|
2
|
|
|
|
|
4
|
my @ready; |
|
320
|
|
|
|
|
|
|
out: |
|
321
|
2
|
|
|
|
|
21
|
while( @ready = $select->can_read(@this_wait)) { |
|
322
|
3
|
|
|
|
|
14019
|
my $fh; |
|
323
|
3
|
|
|
|
|
27
|
foreach $fh (@ready) { |
|
324
|
3
|
|
|
|
|
12
|
my $buf; |
|
325
|
3
|
|
|
|
|
165
|
my $len = $fh->sysread($buf, 2048); |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
# XXX Linux is fond of returning EAGAIN, which we'll need |
|
328
|
|
|
|
|
|
|
# to check for here. Still true for sysread? |
|
329
|
3
|
50
|
|
|
|
280
|
if(!defined($len)) { |
|
330
|
0
|
|
|
|
|
0
|
my $errno = int($!); |
|
331
|
0
|
0
|
0
|
|
|
0
|
next out if(($errno == EAGAIN) || ($errno == EINTR)); |
|
332
|
0
|
|
|
|
|
0
|
die "read error: $!"; |
|
333
|
|
|
|
|
|
|
} |
|
334
|
3
|
100
|
|
|
|
19
|
last out if(!$len); # EOF |
|
335
|
|
|
|
|
|
|
|
|
336
|
2
|
|
|
|
|
47
|
$self->text($self->text() . $buf); |
|
337
|
|
|
|
|
|
|
} |
|
338
|
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
# Adjust select timer if we need to. |
|
340
|
2
|
100
|
|
|
|
57
|
if(defined($total_wait)) { |
|
341
|
1
|
|
|
|
|
14
|
my $now = time(); |
|
342
|
1
|
50
|
|
|
|
8
|
last out if($now >= $end_time); |
|
343
|
1
|
|
|
|
|
10
|
$this_wait[0] = $end_time - $now; |
|
344
|
|
|
|
|
|
|
} |
|
345
|
|
|
|
|
|
|
} |
|
346
|
2
|
100
|
|
|
|
1990508
|
if(defined($total_wait)) { |
|
347
|
1
|
|
|
|
|
8
|
my $now = time(); |
|
348
|
1
|
50
|
|
|
|
19
|
$self->stat(TRACEROUTE_TIMEOUT) if($now >= $end_time); |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
# This is exceedingly dubious. Crawl into IO::Pipe::End's |
|
351
|
|
|
|
|
|
|
# innards, and nuke the pid connected to our pipe. Otherwise, |
|
352
|
|
|
|
|
|
|
# close will call waitpid, which we certainly don't wait for a |
|
353
|
|
|
|
|
|
|
# timeout. |
|
354
|
1
|
|
|
|
|
2
|
delete ${*$tr_pipe}{io_pipe_pid}; |
|
|
1
|
|
|
|
|
10
|
|
|
355
|
|
|
|
|
|
|
} |
|
356
|
|
|
|
|
|
|
|
|
357
|
2
|
|
|
|
|
19
|
$tr_pipe->close(); |
|
358
|
|
|
|
|
|
|
|
|
359
|
2
|
|
|
|
|
142
|
my $accum = $self->text(); |
|
360
|
2
|
50
|
|
|
|
27
|
die "No output from traceroute. Exec failure?" if($accum eq ""); |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
# Do the grunt parsing work |
|
363
|
2
|
|
|
|
|
18
|
$self->_parse($accum); |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
# XXX are you really sure you want to do it like this?? |
|
366
|
2
|
100
|
|
|
|
15
|
if($self->stat() != TRACEROUTE_TIMEOUT) { |
|
367
|
1
|
|
|
|
|
4
|
$self->stat(TRACEROUTE_OK); |
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
|
|
370
|
2
|
|
|
|
|
58
|
$self; |
|
371
|
|
|
|
|
|
|
} |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
sub parse { |
|
374
|
16
|
|
|
16
|
1
|
88
|
my $self = shift; |
|
375
|
|
|
|
|
|
|
|
|
376
|
16
|
|
|
|
|
52
|
$self->_parse($self->text()); |
|
377
|
|
|
|
|
|
|
} |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
sub argv { |
|
380
|
2
|
|
|
2
|
1
|
6
|
my $self = shift; |
|
381
|
|
|
|
|
|
|
|
|
382
|
2
|
|
|
|
|
4
|
my @tr_args; |
|
383
|
2
|
|
|
|
|
7
|
push(@tr_args, $self->trace_program()); |
|
384
|
2
|
|
|
|
|
20
|
push(@tr_args, $self->_tr_cmd_args()); |
|
385
|
2
|
|
|
|
|
8
|
push(@tr_args, $self->host()); |
|
386
|
2
|
|
33
|
|
|
17
|
my @plen = ($self->packetlen) || (); # Sigh. |
|
387
|
2
|
|
|
|
|
4
|
push(@tr_args, @plen); |
|
388
|
|
|
|
|
|
|
|
|
389
|
2
|
|
|
|
|
13
|
return(@tr_args); |
|
390
|
|
|
|
|
|
|
} |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
## |
|
393
|
|
|
|
|
|
|
# Hop and query functions |
|
394
|
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
sub hops ($) { |
|
396
|
3
|
|
|
3
|
1
|
30
|
my $self = shift; |
|
397
|
|
|
|
|
|
|
|
|
398
|
3
|
|
|
|
|
8
|
my $hop_ary = $self->{"hops"}; |
|
399
|
|
|
|
|
|
|
|
|
400
|
3
|
50
|
|
|
|
11
|
return() unless $hop_ary; |
|
401
|
|
|
|
|
|
|
|
|
402
|
3
|
|
|
|
|
12
|
return(int(@{$hop_ary})); |
|
|
3
|
|
|
|
|
30
|
|
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
sub hop_queries ($$) { |
|
406
|
15
|
|
|
15
|
1
|
7208
|
my $self = shift; |
|
407
|
15
|
|
|
|
|
26
|
my $hop = (shift) - 1; |
|
408
|
|
|
|
|
|
|
|
|
409
|
15
|
|
|
|
|
108
|
$self->{"hops"} && $self->{"hops"}->[$hop] && |
|
410
|
15
|
50
|
33
|
|
|
118
|
int(@{$self->{"hops"}->[$hop]}); |
|
411
|
|
|
|
|
|
|
} |
|
412
|
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
sub found ($) { |
|
414
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
415
|
0
|
|
|
|
|
0
|
my $hops = $self->hops(); |
|
416
|
|
|
|
|
|
|
|
|
417
|
0
|
0
|
|
|
|
0
|
if($hops) { |
|
418
|
0
|
|
|
|
|
0
|
my $last_hop = $self->hop_query_host($hops, 0); |
|
419
|
0
|
|
|
|
|
0
|
my $stat = $self->hop_query_stat($hops, 0); |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
# Is this the correct thing to be doing? This gap in |
|
422
|
|
|
|
|
|
|
# semantics missed me, and wasn't caught until post 1.5 It |
|
423
|
|
|
|
|
|
|
# would be a good to audit the semantics here. It's possible |
|
424
|
|
|
|
|
|
|
# that a prior version change broke this. |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
# Getting good regression tests would be nice, but traceroute |
|
427
|
|
|
|
|
|
|
# is an annoying thing to do regression on -- you usually |
|
428
|
|
|
|
|
|
|
# don't have enough control over the network. If I was good, |
|
429
|
|
|
|
|
|
|
# I would be collecting my bug reports, and saving the |
|
430
|
|
|
|
|
|
|
# traceroute output produced there. |
|
431
|
0
|
0
|
|
|
|
0
|
return(undef) if(!defined($last_hop)); |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
# Ugh, what to do here? |
|
434
|
|
|
|
|
|
|
# In IPv4, a host may send the port-unreachable ICMP from an |
|
435
|
|
|
|
|
|
|
# address other than the one we sent to. (and in fact, I use |
|
436
|
|
|
|
|
|
|
# this feature quite a bit to map out networks) |
|
437
|
|
|
|
|
|
|
# IIRC, IPv6 mandates that the unreachable comes from the address we |
|
438
|
|
|
|
|
|
|
# sent to, so we don't have this problem. |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
# This assumption will that any last hop answer that wasn't an |
|
441
|
|
|
|
|
|
|
# error may bite us. |
|
442
|
0
|
0
|
0
|
|
|
0
|
if( |
|
|
|
|
0
|
|
|
|
|
|
443
|
|
|
|
|
|
|
(($stat == TRACEROUTE_OK) || ($stat == TRACEROUTE_BSDBUG) || |
|
444
|
|
|
|
|
|
|
($stat == TRACEROUTE_UNREACH_PROTO))) { |
|
445
|
0
|
|
|
|
|
0
|
return(1); |
|
446
|
|
|
|
|
|
|
} |
|
447
|
|
|
|
|
|
|
} |
|
448
|
0
|
|
|
|
|
0
|
return(undef); |
|
449
|
|
|
|
|
|
|
} |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
sub hop_query_stat ($$) { |
|
452
|
39
|
|
|
39
|
1
|
1200
|
_query_accessor_common(@_,query_stat_offset); |
|
453
|
|
|
|
|
|
|
} |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
sub hop_query_host ($$) { |
|
456
|
83
|
|
|
83
|
1
|
8715
|
_query_accessor_common(@_,query_host_offset); |
|
457
|
|
|
|
|
|
|
} |
|
458
|
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
sub hop_query_time ($$) { |
|
460
|
32
|
|
|
32
|
1
|
1267
|
_query_accessor_common(@_,query_time_offset); |
|
461
|
|
|
|
|
|
|
} |
|
462
|
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
## |
|
464
|
|
|
|
|
|
|
# Accesssor/mutators for ordinary instance variables. (Read/Write) |
|
465
|
|
|
|
|
|
|
# We generate these. |
|
466
|
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
foreach my $name (@simple_instance_vars) { |
|
468
|
|
|
|
|
|
|
my $sym = qualify_to_ref($name); |
|
469
|
|
|
|
|
|
|
my $code = sub { |
|
470
|
284
|
|
|
284
|
|
1319
|
my $self = shift; |
|
471
|
284
|
|
|
|
|
566
|
my $old = $self->{$name}; |
|
472
|
284
|
100
|
|
|
|
672
|
$self->{$name} = $_[0] if @_; |
|
473
|
284
|
|
|
|
|
1142
|
return $old; |
|
474
|
|
|
|
|
|
|
}; |
|
475
|
|
|
|
|
|
|
*{$sym} = $code; |
|
476
|
|
|
|
|
|
|
} |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
### |
|
479
|
|
|
|
|
|
|
# Various internal methods |
|
480
|
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
# Many of these would be useful to override in a derived class. |
|
482
|
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
# Build and return the pipe that talks to our child traceroute. |
|
484
|
|
|
|
|
|
|
sub _make_pipe ($) { |
|
485
|
2
|
|
|
2
|
|
10
|
my $self = shift; |
|
486
|
|
|
|
|
|
|
|
|
487
|
2
|
|
|
|
|
17
|
$self->debug_print(9, Dumper($self)); |
|
488
|
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
# XXX we probably shouldn't throw stderr away. |
|
490
|
2
|
|
|
|
|
69
|
open(my $savestderr, ">&", STDERR); |
|
491
|
2
|
|
|
|
|
113
|
open(STDERR, ">", "/dev/null"); |
|
492
|
|
|
|
|
|
|
|
|
493
|
2
|
|
|
|
|
22
|
my $pipe = new IO::Pipe; |
|
494
|
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
# IO::Pipe is very unhelpful about error catching. It calls die |
|
496
|
|
|
|
|
|
|
# in the child program, but returns a reasonable looking object in |
|
497
|
|
|
|
|
|
|
# the parent. This is really a standard unix fork/exec issue, but |
|
498
|
|
|
|
|
|
|
# the library doesn't help us. |
|
499
|
2
|
|
|
|
|
244
|
my $result = $pipe->reader($self->argv()); |
|
500
|
|
|
|
|
|
|
|
|
501
|
2
|
|
|
|
|
3543
|
open(STDERR, ">&", $savestderr); |
|
502
|
2
|
|
|
|
|
32
|
close($savestderr); |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
# Long standing bug; the pipe needs to be marked non blocking. |
|
505
|
2
|
|
|
|
|
90
|
$result->blocking(0); |
|
506
|
|
|
|
|
|
|
|
|
507
|
2
|
|
|
|
|
83
|
$result; |
|
508
|
|
|
|
|
|
|
} |
|
509
|
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
# Map some instance variables to command line arguments that take |
|
511
|
|
|
|
|
|
|
# arguments. |
|
512
|
|
|
|
|
|
|
my %cmdline_valuemap = |
|
513
|
|
|
|
|
|
|
( "base_port" => "-p", |
|
514
|
|
|
|
|
|
|
"max_ttl" => "-m", |
|
515
|
|
|
|
|
|
|
"queries" => "-q", |
|
516
|
|
|
|
|
|
|
"query_timeout" => "-w", |
|
517
|
|
|
|
|
|
|
"source_address" => "-s", |
|
518
|
|
|
|
|
|
|
"tos" => "-t", |
|
519
|
|
|
|
|
|
|
); |
|
520
|
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
# Map more instance variables to command line arguments that are |
|
522
|
|
|
|
|
|
|
# flags. |
|
523
|
|
|
|
|
|
|
my %cmdline_flagmap = |
|
524
|
|
|
|
|
|
|
( "no_fragment" => "-F", |
|
525
|
|
|
|
|
|
|
"use_icmp" => "-I", |
|
526
|
|
|
|
|
|
|
"use_tcp" => "-T" |
|
527
|
|
|
|
|
|
|
); |
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
# Build a list of command line arguments |
|
530
|
|
|
|
|
|
|
sub _tr_cmd_args ($) { |
|
531
|
2
|
|
|
2
|
|
5
|
my $self = shift; |
|
532
|
|
|
|
|
|
|
|
|
533
|
2
|
|
|
|
|
5
|
my @result; |
|
534
|
|
|
|
|
|
|
|
|
535
|
2
|
|
|
|
|
4
|
push(@result, "-n"); |
|
536
|
|
|
|
|
|
|
|
|
537
|
2
|
|
|
|
|
4
|
my($key, $flag); |
|
538
|
|
|
|
|
|
|
|
|
539
|
2
|
|
|
|
|
13
|
while(($key, $flag) = each %cmdline_flagmap) { |
|
540
|
6
|
50
|
|
|
|
64
|
push(@result, $flag) if($self->$key());; |
|
541
|
|
|
|
|
|
|
} |
|
542
|
|
|
|
|
|
|
|
|
543
|
2
|
|
|
|
|
10
|
while(($key, $flag) = each %cmdline_valuemap) { |
|
544
|
12
|
|
|
|
|
40
|
my $val = $self->$key(); |
|
545
|
12
|
50
|
|
|
|
46
|
if(defined $val) { |
|
546
|
0
|
|
|
|
|
0
|
push(@result, $flag, $val); |
|
547
|
|
|
|
|
|
|
} |
|
548
|
|
|
|
|
|
|
} |
|
549
|
|
|
|
|
|
|
|
|
550
|
2
|
|
|
|
|
6
|
@result; |
|
551
|
|
|
|
|
|
|
} |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
# Do the grunt work of parsing the output. |
|
554
|
|
|
|
|
|
|
sub _parse ($$) { |
|
555
|
18
|
|
|
18
|
|
45
|
my $self = shift; |
|
556
|
18
|
|
|
|
|
39
|
my $tr_output = shift; |
|
557
|
|
|
|
|
|
|
|
|
558
|
18
|
|
|
|
|
62
|
my $hopno; |
|
559
|
|
|
|
|
|
|
my $query; |
|
560
|
|
|
|
|
|
|
|
|
561
|
0
|
|
|
|
|
0
|
my $icmp_map; |
|
562
|
0
|
|
|
|
|
0
|
my $icmp_map_re; |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
my $set_icmp_map = sub { |
|
565
|
202
|
100
|
|
202
|
|
406
|
$icmp_map = shift if(!defined($icmp_map));; |
|
566
|
202
|
|
|
|
|
205
|
$icmp_map_re = join("", keys(%{$icmp_map})); |
|
|
202
|
|
|
|
|
867
|
|
|
567
|
18
|
|
|
|
|
122
|
}; |
|
568
|
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
# This is a crufty hand coded parser that does its job well |
|
570
|
|
|
|
|
|
|
# enough. The approach of regular expressions without state is |
|
571
|
|
|
|
|
|
|
# far from perfect, but it gets the job done. |
|
572
|
|
|
|
|
|
|
line: |
|
573
|
18
|
|
|
|
|
208
|
foreach $_ (split(/\n/, $tr_output)) { |
|
574
|
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
# Some traceroutes appear to print informational line to stdout, |
|
576
|
|
|
|
|
|
|
# and we don't care. |
|
577
|
225
|
100
|
|
|
|
881
|
/^traceroute to / && next; |
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
# AIX 5L has to be different. |
|
580
|
224
|
50
|
|
|
|
549
|
/^trying to get / && next; |
|
581
|
224
|
50
|
|
|
|
426
|
/^source should be / && next; |
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
# NetBSD's traceroute emits info about path MTU discovery if |
|
584
|
|
|
|
|
|
|
# you want, don't know who else does this. |
|
585
|
224
|
50
|
|
|
|
392
|
/^message too big, trying new MTU = (\d+)/ && do { |
|
586
|
0
|
|
|
|
|
0
|
$self->pathmtu($1); |
|
587
|
0
|
|
|
|
|
0
|
next; |
|
588
|
|
|
|
|
|
|
}; |
|
589
|
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
# For now, discard MPLS label stack information emitted by |
|
591
|
|
|
|
|
|
|
# some vendor's traceroutes. Once I'm sure I'm sure I |
|
592
|
|
|
|
|
|
|
# understand the semantics offered by both the underlying MPLS |
|
593
|
|
|
|
|
|
|
# and whatever crazy limits the MPLS patch has, I can think |
|
594
|
|
|
|
|
|
|
# about an interface. My reading of the code is that you will |
|
595
|
|
|
|
|
|
|
# get the label stack of the last query. If this isn't |
|
596
|
|
|
|
|
|
|
# representative of all of the queries, it sucks to be you. |
|
597
|
|
|
|
|
|
|
# You can still get what you need, but it would be nice if the |
|
598
|
|
|
|
|
|
|
# tool didn't throw information away... |
|
599
|
|
|
|
|
|
|
# possibilities. |
|
600
|
224
|
50
|
|
|
|
702
|
/^\s+MPLS Label=(\d+) CoS=(\d) TTL=(\d+) S=(\d+)/ && next; |
|
601
|
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
# Cisco chatter. We use the "Type escape sequence..." line to |
|
603
|
|
|
|
|
|
|
# set the icmp_map to cisco. |
|
604
|
224
|
100
|
|
|
|
470
|
/^Type escape sequence to abort/ && do { |
|
605
|
9
|
|
|
|
|
19
|
&{$set_icmp_map}(\%icmp_map_cisco); |
|
|
9
|
|
|
|
|
30
|
|
|
606
|
9
|
|
|
|
|
24
|
next; |
|
607
|
|
|
|
|
|
|
}; |
|
608
|
215
|
100
|
|
|
|
382
|
/^Tracing the route to/ && next; |
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
# XXX there's one like this in the query loop, too. |
|
611
|
|
|
|
|
|
|
# Can we eliminate one? |
|
612
|
206
|
100
|
|
|
|
528
|
/^$/ && next; |
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
# Cisco marks ECMP paths very differently from LBL. LBL |
|
615
|
|
|
|
|
|
|
# outputs the changing addresses in one line, whereas cisco |
|
616
|
|
|
|
|
|
|
# will output a line with no hop count. |
|
617
|
|
|
|
|
|
|
# XXX we probably need to possibly match DNS in here. |
|
618
|
188
|
100
|
|
|
|
697
|
s/^\s{4}(\d+\.\d+\.\d+\.\d+ )/$1/ && goto query; |
|
619
|
180
|
100
|
|
|
|
480
|
s/^\s{4}([0-9a-fA-F:]*:[0-9a-fA-F]*(?:\.\d+\.\d+\.\d+)?)/$1/ && |
|
620
|
|
|
|
|
|
|
goto query; |
|
621
|
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
# Each line starts with the hopno (space padded to two characters) |
|
623
|
|
|
|
|
|
|
# and a space. |
|
624
|
174
|
50
|
|
|
|
2498
|
s/^ ?([0-9 ][0-9]) // || die "Can't find hop number in output: $_"; |
|
625
|
|
|
|
|
|
|
|
|
626
|
174
|
|
|
|
|
385
|
$hopno = $1 + 0; |
|
627
|
174
|
|
|
|
|
189
|
$query = 1; |
|
628
|
|
|
|
|
|
|
|
|
629
|
174
|
|
|
|
|
195
|
my $addr; |
|
630
|
|
|
|
|
|
|
my $time; |
|
631
|
|
|
|
|
|
|
|
|
632
|
0
|
|
|
|
|
0
|
my $last_token; |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
query: |
|
635
|
188
|
|
|
|
|
440
|
while($_) { |
|
636
|
|
|
|
|
|
|
# dns name and address; rewrite as just an address |
|
637
|
|
|
|
|
|
|
# XXX should keep dns name |
|
638
|
721
|
|
|
|
|
928
|
s/^ ?[-A-Za-z0-9.]+ \((\d+\.\d+\.\d+\.\d+)\)/$1/; |
|
639
|
721
|
|
|
|
|
849
|
s/^ ?[-A-Za-z0-9.]+ \(([0-9a-fA-F:]*:[0-9a-fA-F]*(?:\.\d+\.\d+\.\d+)?)\)/$1/; |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
# ip address of a response |
|
642
|
721
|
100
|
|
|
|
3018
|
s/^ ?(\d+\.\d+\.\d+\.\d+)// && do { |
|
643
|
115
|
|
|
|
|
224
|
$last_token = token_addr; |
|
644
|
115
|
|
|
|
|
195
|
$addr = $1; |
|
645
|
115
|
|
|
|
|
150
|
&{$set_icmp_map}(\%icmp_map_v4); |
|
|
115
|
|
|
|
|
258
|
|
|
646
|
115
|
|
|
|
|
717
|
next query; |
|
647
|
|
|
|
|
|
|
}; |
|
648
|
|
|
|
|
|
|
# ipv6 address of a response. This regexp is sleazy. |
|
649
|
606
|
100
|
|
|
|
1791
|
s/^ ?([0-9a-fA-F:]*:[0-9a-fA-F]*(?:\.\d+\.\d+\.\d+)?)// && do { |
|
650
|
78
|
|
|
|
|
102
|
$last_token = token_addr; |
|
651
|
78
|
|
|
|
|
126
|
$addr = $1; |
|
652
|
78
|
|
|
|
|
108
|
&{$set_icmp_map}(\%icmp_map_v6); |
|
|
78
|
|
|
|
|
149
|
|
|
653
|
78
|
|
|
|
|
219
|
next query; |
|
654
|
|
|
|
|
|
|
}; |
|
655
|
|
|
|
|
|
|
# Redhat FC5 traceroute does this; it's redundant. |
|
656
|
528
|
50
|
|
|
|
1277
|
s/^ \((\d+\.\d+\.\d+\.\d+)\)// && next query; |
|
657
|
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
# round trip time of query |
|
659
|
528
|
100
|
|
|
|
2423
|
s/^ ? ?([0-9.]+) ms(?:ec)?// && do { |
|
660
|
495
|
|
|
|
|
575
|
$last_token = token_time; |
|
661
|
495
|
|
|
|
|
1322
|
$time = $1 + 0; |
|
662
|
|
|
|
|
|
|
|
|
663
|
495
|
|
|
|
|
893
|
$self->_add_hop_query($hopno, $query, |
|
664
|
|
|
|
|
|
|
TRACEROUTE_OK, $addr, $time); |
|
665
|
495
|
|
|
|
|
587
|
$query++; |
|
666
|
495
|
|
|
|
|
1280
|
next query; |
|
667
|
|
|
|
|
|
|
}; |
|
668
|
|
|
|
|
|
|
# query timed out |
|
669
|
33
|
100
|
|
|
|
127
|
s/^ +\*// && do { |
|
670
|
16
|
|
|
|
|
21
|
$last_token = token_time; |
|
671
|
16
|
|
|
|
|
42
|
$self->_add_hop_query($hopno, $query, |
|
672
|
|
|
|
|
|
|
TRACEROUTE_TIMEOUT, |
|
673
|
|
|
|
|
|
|
inet_ntoa(INADDR_NONE), 0); |
|
674
|
16
|
|
|
|
|
20
|
$query++; |
|
675
|
16
|
|
|
|
|
40
|
next query; |
|
676
|
|
|
|
|
|
|
}; |
|
677
|
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
# extra information from the probe (random ICMP info |
|
679
|
|
|
|
|
|
|
# and such). |
|
680
|
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
# There was a bug in this regexp prior to 1.09; reorder |
|
682
|
|
|
|
|
|
|
# the clauses and everything gets better. |
|
683
|
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
# Note that this is actually a very subtle DWIM on perl's |
|
685
|
|
|
|
|
|
|
# part: in "pure" regular expression theory, order of |
|
686
|
|
|
|
|
|
|
# expression doesn't matter; the resultant DFA has no |
|
687
|
|
|
|
|
|
|
# order concept. Without perl DWIMing on our regexp, we'd |
|
688
|
|
|
|
|
|
|
# write the regexp and code to perform a token lookahead: |
|
689
|
|
|
|
|
|
|
# the transitions after ! would be < for digits, the keys |
|
690
|
|
|
|
|
|
|
# of icmp map, and finally whitespace or end of string |
|
691
|
|
|
|
|
|
|
# indicate a lone "!". |
|
692
|
|
|
|
|
|
|
|
|
693
|
17
|
50
|
|
|
|
371
|
s/^ (!<\d+>|\?|![$icmp_map_re]?) ?// && do { |
|
694
|
17
|
|
|
|
|
35
|
my $flag = $1; |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
# If the prior token was a time sample, it incremented |
|
697
|
|
|
|
|
|
|
# query. Undo that locally. |
|
698
|
17
|
|
|
|
|
24
|
my $lquery = $query; |
|
699
|
17
|
100
|
100
|
|
|
99
|
$lquery-- if(defined($last_token) && $last_token == token_time); |
|
700
|
|
|
|
|
|
|
|
|
701
|
17
|
|
|
|
|
22
|
my $stat; |
|
702
|
17
|
50
|
|
|
|
182
|
if($flag =~ /^!<\d>$/) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
703
|
0
|
|
|
|
|
0
|
$stat = TRACEROUTE_UNKNOWN; |
|
704
|
|
|
|
|
|
|
} elsif($flag =~ /^!$/) { |
|
705
|
0
|
|
|
|
|
0
|
$stat = $icmp_map->{"!"}; |
|
706
|
|
|
|
|
|
|
} elsif($flag =~ /^!([$icmp_map_re])$/) { |
|
707
|
12
|
|
|
|
|
27
|
my $icmp = $1; |
|
708
|
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
# Shouldn't happen |
|
710
|
12
|
50
|
|
|
|
34
|
die "Unable to parse traceroute output (flag $icmp)!" |
|
711
|
|
|
|
|
|
|
unless(defined($icmp_map->{$icmp})); |
|
712
|
|
|
|
|
|
|
|
|
713
|
12
|
|
|
|
|
22
|
$stat = $icmp_map->{$icmp}; |
|
714
|
|
|
|
|
|
|
} elsif($flag eq "?") { |
|
715
|
|
|
|
|
|
|
# Cisco does this. |
|
716
|
5
|
|
|
|
|
7
|
$stat = TRACEROUTE_UNKNOWN; |
|
717
|
|
|
|
|
|
|
} else { |
|
718
|
0
|
|
|
|
|
0
|
die "unrecognized flag: $flag"; |
|
719
|
|
|
|
|
|
|
} |
|
720
|
|
|
|
|
|
|
|
|
721
|
17
|
100
|
100
|
|
|
92
|
if(defined($last_token) && ($last_token == token_time)) { |
|
722
|
7
|
|
|
|
|
26
|
$self->_change_hop_query_stat($hopno, $lquery, $stat); |
|
723
|
|
|
|
|
|
|
} else { |
|
724
|
10
|
|
|
|
|
32
|
$self->_add_hop_query($hopno, $lquery, $stat, $addr, 0); |
|
725
|
10
|
|
|
|
|
15
|
$query++; |
|
726
|
|
|
|
|
|
|
} |
|
727
|
17
|
|
|
|
|
19
|
$last_token = token_flag; |
|
728
|
|
|
|
|
|
|
|
|
729
|
17
|
|
|
|
|
82
|
next query; |
|
730
|
|
|
|
|
|
|
}; |
|
731
|
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
# Nothing left, next line. |
|
733
|
0
|
0
|
|
|
|
0
|
/^$/ && next line; |
|
734
|
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
# Cisco ASN data. |
|
736
|
|
|
|
|
|
|
# XXX we should keep this. |
|
737
|
0
|
0
|
|
|
|
0
|
s/^ \[AS \d+\]// && next query; |
|
738
|
|
|
|
|
|
|
|
|
739
|
0
|
0
|
|
|
|
0
|
s/ \[MPLS: Label \d+ Exp \d+\]// && next query; |
|
740
|
0
|
0
|
|
|
|
0
|
s, \[MPLS: Labels \d+(?:/\d+)* Exp \d+\],, && next query; |
|
741
|
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
# Some LBL derived traceroutes print ttl stuff |
|
743
|
0
|
0
|
|
|
|
0
|
s/^ \(ttl ?= ?\d+!\)// && next query; |
|
744
|
|
|
|
|
|
|
|
|
745
|
0
|
|
|
|
|
0
|
die "Unable to parse traceroute output: $_"; |
|
746
|
|
|
|
|
|
|
} |
|
747
|
|
|
|
|
|
|
} |
|
748
|
|
|
|
|
|
|
} |
|
749
|
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
sub _zero_text_accumulator ($) { |
|
751
|
2
|
|
|
2
|
|
15
|
my $self = shift; |
|
752
|
2
|
|
|
|
|
31
|
my $elem = "text"; |
|
753
|
|
|
|
|
|
|
|
|
754
|
2
|
|
|
|
|
73
|
$self->{$elem} = ""; |
|
755
|
|
|
|
|
|
|
} |
|
756
|
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
# Hop stuff |
|
758
|
|
|
|
|
|
|
sub _zero_hops ($) { |
|
759
|
2
|
|
|
2
|
|
18
|
my $self = shift; |
|
760
|
|
|
|
|
|
|
|
|
761
|
2
|
|
|
|
|
20
|
delete $self->{"hops"}; |
|
762
|
|
|
|
|
|
|
} |
|
763
|
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
sub _add_hop_query ($$$$$$) { |
|
765
|
521
|
|
|
521
|
|
1243
|
my $self = shift; |
|
766
|
|
|
|
|
|
|
|
|
767
|
521
|
|
|
|
|
607
|
my $hop = (shift) - 1; |
|
768
|
521
|
|
|
|
|
585
|
my $query = (shift) - 1; |
|
769
|
|
|
|
|
|
|
|
|
770
|
521
|
|
|
|
|
486
|
my $stat = shift; |
|
771
|
521
|
|
|
|
|
583
|
my $host = shift; |
|
772
|
521
|
|
|
|
|
512
|
my $time = shift; |
|
773
|
|
|
|
|
|
|
|
|
774
|
521
|
|
|
|
|
1998
|
$self->{"hops"}->[$hop]->[$query] = [ $stat, $host, $time ]; |
|
775
|
|
|
|
|
|
|
} |
|
776
|
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
sub _change_hop_query_stat ($$$$) { |
|
778
|
7
|
|
|
7
|
|
9
|
my $self = shift; |
|
779
|
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
# Zero base these |
|
781
|
7
|
|
|
|
|
14
|
my $hop = (shift) - 1; |
|
782
|
7
|
|
|
|
|
11
|
my $query = (shift) - 1; |
|
783
|
|
|
|
|
|
|
|
|
784
|
7
|
|
|
|
|
9
|
my $stat = shift; |
|
785
|
|
|
|
|
|
|
|
|
786
|
7
|
|
|
|
|
20
|
$self->{"hops"}->[$hop]->[$query]->[ query_stat_offset ] = $stat; |
|
787
|
|
|
|
|
|
|
} |
|
788
|
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
sub _query_accessor_common ($$$) { |
|
790
|
154
|
|
|
154
|
|
225
|
my $self = shift; |
|
791
|
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
# Zero base these |
|
793
|
154
|
|
|
|
|
273
|
my $hop = (shift) - 1; |
|
794
|
154
|
|
|
|
|
192
|
my $query = (shift) - 1; |
|
795
|
|
|
|
|
|
|
|
|
796
|
154
|
|
|
|
|
182
|
my $which_one = shift; |
|
797
|
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
# Deal with wildcard |
|
799
|
154
|
100
|
|
|
|
319
|
if($query == -1) { |
|
800
|
5
|
|
|
|
|
11
|
my $query_stat; |
|
801
|
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
my $aref; |
|
803
|
5
|
|
|
|
|
21
|
query: |
|
804
|
5
|
|
|
|
|
10
|
foreach $aref (@{$self->{"hops"}->[$hop]}) { |
|
805
|
11
|
|
|
|
|
18
|
$query_stat = $aref->[query_stat_offset]; |
|
806
|
11
|
100
|
|
|
|
22
|
$query_stat == TRACEROUTE_TIMEOUT && do { next query }; |
|
|
8
|
|
|
|
|
17
|
|
|
807
|
3
|
50
|
|
|
|
49
|
$query_stat == TRACEROUTE_UNKNOWN && do { next query }; |
|
|
0
|
|
|
|
|
0
|
|
|
808
|
3
|
|
|
|
|
5
|
do { return $aref->[$which_one] }; |
|
|
3
|
|
|
|
|
18
|
|
|
809
|
|
|
|
|
|
|
} |
|
810
|
2
|
|
|
|
|
12
|
return undef; |
|
811
|
|
|
|
|
|
|
} else { |
|
812
|
149
|
|
|
|
|
848
|
$self->{"hops"}->[$hop]->[$query]->[$which_one]; |
|
813
|
|
|
|
|
|
|
} |
|
814
|
|
|
|
|
|
|
} |
|
815
|
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
sub debug_print ($$$;@) { |
|
817
|
49
|
|
|
49
|
0
|
4454
|
my $self = shift; |
|
818
|
49
|
|
|
|
|
76
|
my $level = shift; |
|
819
|
49
|
|
|
|
|
78
|
my $fmtstring = shift; |
|
820
|
|
|
|
|
|
|
|
|
821
|
49
|
50
|
|
|
|
129
|
return unless $self->debug() >= $level; |
|
822
|
|
|
|
|
|
|
|
|
823
|
0
|
|
|
|
|
|
my($package, $filename, $line, $subroutine, |
|
824
|
|
|
|
|
|
|
$hasargs, $wantarray, $evaltext, $is_require) = caller(0); |
|
825
|
|
|
|
|
|
|
|
|
826
|
0
|
|
|
|
|
|
my $caller_line = $line; |
|
827
|
0
|
|
|
|
|
|
my $caller_name = $subroutine; |
|
828
|
0
|
|
|
|
|
|
my $caller_file = $filename; |
|
829
|
|
|
|
|
|
|
|
|
830
|
0
|
|
|
|
|
|
my $string = sprintf($fmtstring, @_); |
|
831
|
|
|
|
|
|
|
|
|
832
|
0
|
|
|
|
|
|
my $caller = "${caller_file}:${caller_name}:${caller_line}"; |
|
833
|
|
|
|
|
|
|
|
|
834
|
0
|
|
|
|
|
|
print STDERR "$caller: $string"; |
|
835
|
|
|
|
|
|
|
} |
|
836
|
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
1; |
|
838
|
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
__END__ |