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__ |