scriptroute 0.4.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3d192161acfbf508dfd7d953bc7ec1a219930d15
4
+ data.tar.gz: a3c7cbd73442f9aa4cc263830a71b62023f08355
5
+ SHA512:
6
+ metadata.gz: 3a98db9cec4c8d90da6c8958e68d063d3049c68d88ac612184abfef44a789f8b57120b2325625f3704c770db3a6557aa2ad6336d36655be28067426b0fff69f0
7
+ data.tar.gz: 77be1bb191576fa8890c8395c1d93577e060cfe4a846989c74c51ae24797e3dbf1c78f091dbedc33ef72c3aa78c373ba391cfe1ee1eed3dd88c70815dee82425
data/bin/sr-ally ADDED
@@ -0,0 +1,21 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'scriptroute/ally'
4
+ require 'scriptroute/ipaddr4'
5
+
6
+ if(ARGV.length < 2) then
7
+ puts "ERROR: need two ip addresses to compare."
8
+ exit 3;
9
+ end
10
+
11
+ verdict = Ally.new(ARGV[0], ARGV[1]);
12
+ puts verdict
13
+ exit case verdict.to_s
14
+ when /^ALIAS/
15
+ 0;
16
+ when /^NOT ALIAS/
17
+ 1;
18
+ when /^UNKNOWN/
19
+ 2;
20
+ end
21
+
data/bin/sr-liveness ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'scriptroute'
4
+ require 'scriptroute/liveness'
5
+
6
+ if ARGV.empty? then
7
+ puts "give me hosts to ping"
8
+ end
9
+ Scriptroute::daemon_running_or_exit
10
+ puts Scriptroute::LivenessTest.new(ARGV)
11
+
data/bin/sr-ping-T ADDED
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: Ruby -*-
3
+
4
+ require 'scriptroute'
5
+ require 'scriptroute/packets'
6
+ require 'scriptroute/nameify'
7
+
8
+ $have_socket = begin
9
+ require 'socket'
10
+ true
11
+ rescue
12
+ false
13
+ end
14
+
15
+ Scriptroute::daemon_running_or_exit
16
+
17
+ i = Scriptroute::ICMPecho.new(0)
18
+ # i.ip_dst = 0x805f0218 # poplar
19
+ i.ip_dst = Scriptroute::dns_lookup(ARGV[0])
20
+ # i.ip_dst = 0xc6ca4b65 # www.sdsc.edu
21
+ # rr = RecordRoute_option.new
22
+ # IPOPT_TS_TSONLY = 0
23
+ # IPOPT_TS_TSANDADDR = 1
24
+ # IPOPT_TS_PRESPEC = 3
25
+ rr = Scriptroute::Timestamp_option.new(Scriptroute::IPv4option::IPOPT_TS_TSONLY)
26
+ i.ip_ttl=15
27
+ i.add_option(rr)
28
+
29
+ p = Scriptroute::send_train([ Struct::DelayedPacket.new(0, i) ])
30
+
31
+ # p[0].response.packet.to_bytes.each_byte { |b| puts "%x " % b }
32
+
33
+ if (p[0].response != nil) then
34
+ if ($VERBOSE) then puts p[0].response.packet end
35
+ resp = ""
36
+ begin
37
+ resp = Scriptroute::IPv4.creator(p[0].response.packet.to_bytes)
38
+ resp.ip_options.each { |opt|
39
+ if(opt.is_a?(Scriptroute::Timestamp_option)) then
40
+ if(resp.ip_options[0].routers != nil &&
41
+ resp.ip_options[0].times != nil && resp.ip_options[0].times.length > 0) then
42
+ opt.times.each_index { |i|
43
+ puts Scriptroute.nameify(resp.ip_options[0].routers[i]) + " " + resp.ip_options[0].times[i].to_s
44
+ }
45
+ else
46
+ opt.times.each_index { |i|
47
+ puts resp.ip_options[0].times[i].to_s
48
+ }
49
+ end
50
+ elsif(opt.is_a?(Scriptroute::RecordRoute_option)) then
51
+ opt.routers.each_index { |i|
52
+ puts "%d %s" % [ i+1, Scriptroute.nameify(resp.ip_options[0].routers[i]) ]
53
+ }
54
+ else
55
+ raise "unknown option class %s" % opt.class.to_s
56
+ end
57
+ }
58
+ rescue => e
59
+ puts "failed due to #{e} parsing: #{resp}"
60
+ end
61
+ else
62
+ puts "no response to ping -r from %s" % p[0].probe.packet.ip_dst
63
+ end
64
+
65
+ # .map { |a|
66
+ # a
67
+ #nameify(a)
68
+ #}
69
+
@@ -0,0 +1,51 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'scriptroute'
4
+ require 'scriptroute/rockettrace'
5
+ require 'scriptroute/nameify'
6
+ require 'scriptroute/commando'
7
+
8
+ c = Commando.new(ARGV, # allows substitution by srclient.rb
9
+ [ CommandoVar.new( [ "-S", "--start-ttl" ],
10
+ "hops out to start" ,
11
+ :$StartTTL, 1),
12
+ CommandoVar.new( [ "-n", "--numeric" ],
13
+ "do no hostname lookup or interpretation" ,
14
+ :$Numeric, false),
15
+ CommandoVar.new( [ "-q", "--queries" ],
16
+ "number of probes at each ttl" ,
17
+ :$Repetitions, 3),
18
+ # todo CommandoVar.new( "--use-icmp",
19
+ # todo "use icmp probes instead of udp" ,
20
+ # todo :$UseICMP, false ) ],
21
+ CommandoVar.new( [ "-o", "--output" ],
22
+ "file to use instead of stdout" ,
23
+ :$Output, ""),
24
+ CommandoVar.new( "--use-tcp",
25
+ "use tcp probes instead of udp" ,
26
+ :$UseTCP, false ) ],
27
+ "destination-host")
28
+
29
+ raise "must start with a positive ttl" if($StartTTL <= 0)
30
+ if(ARGV[0] == nil) then
31
+ c.usage
32
+ exit
33
+ end
34
+
35
+ Scriptroute::daemon_running_or_exit
36
+
37
+ if($Output != "") then
38
+ $stdout.reopen(File.open($Output,"a"))
39
+ end
40
+
41
+ Destination = (ARGV[0] =~ /(\d{1,3}\.){3}\d{1,3}/) ? ARGV[0] : Scriptroute.dns_lookup(ARGV[0])
42
+ puts Scriptroute::Rockettrace.new(Destination, $StartTTL, $UseTCP, $Repetitions).to_s(lambda { |x|
43
+ if($Numeric) then
44
+ x
45
+ elsif(x=='' || x==nil)
46
+ "[empty]"
47
+ else
48
+ Scriptroute.nameify(x)
49
+ end
50
+ })
51
+
data/bin/sr-traceroute ADDED
@@ -0,0 +1,35 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'scriptroute'
4
+
5
+ Scriptroute::daemon_running_or_exit
6
+
7
+ probe = Scriptroute::UDP.new(12)
8
+
9
+ probe.ip_dst = Scriptroute::dns_lookup(ARGV[0])
10
+
11
+ unreach = false
12
+
13
+ puts "Traceroute to #{ARGV[0]} (#{probe.ip_dst})"
14
+
15
+ catch(:unreachable) do
16
+ ( 1..64 ).each { |ttl|
17
+ ( 1..3 ).each { |rep|
18
+ probe.ip_ttl = ttl
19
+ # some boxes refuse to reply to additional probes to the same uh_dport.
20
+ probe.uh_dport = 33434 + ttl + rep
21
+ packets = Scriptroute::send_train([ Struct::DelayedPacket.new(0,probe) ])
22
+ response = (packets[0].response) ? packets[0].response.packet : nil
23
+ if(response) then
24
+ puts '%d %s %5.3f ms' % [ ttl, response.ip_src, packets[0].rtt * 1000.0 ]
25
+ if(response.is_a?(Scriptroute::ICMP)) then
26
+ unreach = true if(response.icmp_type == Scriptroute::ICMP::ICMP_UNREACH)
27
+ end
28
+ else
29
+ puts '%d *' % [ ttl ]
30
+ end
31
+ $stdout.flush
32
+ }
33
+ throw :unreachable if(unreach)
34
+ }
35
+ end
data/bin/tulip ADDED
@@ -0,0 +1,183 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ # require "srclient";
4
+ require "scriptroute";
5
+ require "scriptroute/tulip/helper.rb";
6
+ require "scriptroute/tulip/reordering.rb";
7
+ require "scriptroute/tulip/loss.rb";
8
+ require "scriptroute/tulip/queuing.rb";
9
+
10
+ FIXCLOCK = "/usr/bin/awk -f scriptroute/fixclock";
11
+
12
+ ################### defaults ################
13
+
14
+ $count = 1000;
15
+ $lag = 1000;
16
+ $spread = 0;
17
+ $start = 1;
18
+ $end = 30;
19
+
20
+ $skipRTrtrs = true;
21
+ $printFrequency = -1;
22
+ $allhops = true;
23
+ $dstonly = false;
24
+ $nonames = false;
25
+ $prefixpath = 0;
26
+
27
+ $verbose = 0;
28
+ $version = "0.0.1"
29
+
30
+ MAX_TRAIN = 4000;
31
+
32
+ #################### usage ##################
33
+ def usage()
34
+ puts "usage: tulip <reordering|loss|queuing> [options] <destination>\n";
35
+ puts "options:";
36
+ puts "--count <num>: number of measurements to use [#{$count}]";
37
+ puts "--lag <lag>: milliseconds to wait between successive measurements of the path [#{$lag}]";
38
+ puts "--spread <spread>: millisecond separation between probes in a single measurement [#{$spread}]";
39
+ puts "--start <start>: hop number to start diagnosis from [#{$start}]";
40
+ puts "--end <end>: hop number to end diagnosis at [#{$end}]";
41
+ puts "";
42
+ puts "--noskip: stop querying rate-limiting routers [skipping]";
43
+ puts "--mode <parallel|binary>: binary search not yet implemented";
44
+ puts "--printfrequency <frequency>: number of measurements to print the analysis [at the end]";
45
+ puts "--noallhops: use round trip measurements for hops that don't support forward primitives [on]";
46
+ puts "--dstonly: only probe end to end path [off]";
47
+ puts "--nonames: don't resolve ip addresses [resolve]";
48
+ puts "--prefixpath <hops>: number of initial hops to excuse for local load balancing [#{$prefixpath}]";
49
+ puts "";
50
+ puts "--help: display this message";
51
+ puts "--verbose <level>: level of verbosity (0-10) [#{$verbose}]";
52
+ puts "--version: print version information and exit";
53
+ puts "";
54
+ exit 1;
55
+ end
56
+
57
+ def printVersionAndExit()
58
+ puts "tulip version: #{$version}";
59
+ exit 0;
60
+ end
61
+
62
+ #################### command line processing #############
63
+
64
+ printVersionAndExit() if (ARGV.index("--version"));
65
+ usage() if (ARGV.length < 2);
66
+
67
+ pathology = ARGV.shift();
68
+ if (pathology[0,1] == "r") then
69
+ pathology = "reordering";
70
+ elsif (pathology[0,1] == "l") then
71
+ pathology = "loss";
72
+ elsif (pathology[0,1] == "q") then
73
+ pathology = "queuing";
74
+ else
75
+ puts "ERROR: unknown pathology";
76
+ usage();
77
+ end
78
+
79
+ switch = ARGV.shift();
80
+ while (switch and switch[0,2] == "--")
81
+ if (switch == "--count") then $count = ARGV.shift.to_i;
82
+ elsif (switch == "--lag") then $lag = ARGV.shift.to_i;
83
+ elsif (switch == "--spread") then $spread = ARGV.shift.to_f;
84
+ elsif (switch == "--start") then $start = ARGV.shift.to_i;
85
+ elsif (switch == "--end") then $end = ARGV.shift.to_i;
86
+ elsif (switch == "--printfrequency") then $printFrequency = ARGV.shift.to_i;
87
+ elsif (switch == "--help") then usage();
88
+ elsif (switch == "--verbose") then $verbose = ARGV.shift.to_i;
89
+ elsif (switch == "--noskip") then $skipRTrtrs = false;
90
+ elsif (switch == "--noallhops") then $allhops = false;
91
+ elsif (switch == "--dstonly") then $dstonly = true;
92
+ elsif (switch == "--nonames") then $nonames = true;
93
+ elsif (switch == "--prefixpath") then $prefixpath = ARGV.shift.to_i;
94
+ else
95
+ puts "ERROR: unknown option\n";
96
+ usage();
97
+ end
98
+ switch = ARGV.shift();
99
+ end
100
+
101
+ Scriptroute::daemon_running_or_exit
102
+
103
+ begin
104
+ destination = IPSocket.getaddress(switch) or switch;
105
+ rescue SocketError
106
+ puts "ERROR: unknown destination '#{switch}'";
107
+ usage();
108
+ end
109
+
110
+ if ($verbose>0) then
111
+ puts "pathology=%s count=%d lag=%d spread=%d start=%d end=%d verbose=%d destination=%s allhops=%s" % [pathology, $count, $lag, $spread, $start, $end, $verbose, destination, $allhops];
112
+ end
113
+
114
+ ########## determine hop characteristics #########
115
+
116
+ puts "tracing to #{destination} ......" if ($verbose >= 1);
117
+ tpath = Scriptroute::Tulip::TracePath.new(destination, !$nonames);
118
+
119
+ #todo: get rid of this ugly hack
120
+ $global_tpath = tpath;
121
+
122
+ puts " ----- pathto #{destination} ----- \n#{tpath.to_s}\n";
123
+ if (tpath.status_code == "incomplete") then
124
+ puts "incomplete trace\n";
125
+ end
126
+
127
+ puts "discovering routers that support forward path diagnosis .....\n" if ($verbose >=1);
128
+ hopDetails = Array.new();
129
+ if ($dstonly) then
130
+ hopDetails[tpath.path.length-1] = [destination, 255, "udp", true, destination];
131
+ else
132
+ middle = $start;
133
+ while (middle <= [$end, tpath.path.length - 1].min)
134
+
135
+ router, ttl = tpath.path[middle].ip, tpath.path[middle].hop;
136
+
137
+ ## for loss and reordering (ip-ids) ##
138
+ if (pathology == "reordering" || pathology == "loss") then
139
+ if (Scriptroute::Tulip::LangChecker.increasingIPIDs(destination, ttl, "udp")) then
140
+ hopDetails[middle] = [destination, ttl, "udp", true, router];
141
+ else
142
+ subpath = Scriptroute::Tulip::TracePath.new(router);
143
+ subset = tpath.subset(subpath);
144
+ puts " ----- subpathto #{router} (#{middle}) is #{subset} -----\n#{subpath.to_s}\n" if ($verbose>0);
145
+ if (subset) then
146
+ type, canCheckForward = Scriptroute::Tulip::LangChecker.getBestOption4IPIDs(router, "echo", "udp", "tcp", "tstamp");
147
+ if (canCheckForward) then
148
+ hopDetails[middle] = [router, 255, type, true, router]
149
+ else
150
+ hopDetails[middle] = [destination, ttl, "udp", false, router] if ($allhops or middle == tpath.path.length - 1);
151
+ end
152
+ else
153
+ hopDetails[middle] = [destination, ttl, "udp", false, router] if ($allhops or middle == tpath.path.length - 1);
154
+ end
155
+ end
156
+ ## queuing (timestamps)
157
+ else
158
+ if Scriptroute::Tulip::(LangChecker.getBestOption(router, "tstamp")) then
159
+ subpath = Scriptroute::Tulip::TracePath.new(router);
160
+ subset = tpath.subset(subpath);
161
+ puts " ----- subpathto #{router} (#{middle}) is #{subset} -----\n#{subpath.to_s}\n" if ($verbose>0);
162
+ if (subset) then
163
+ hopDetails[middle] = [router, 255, "tstamp", true, router];
164
+ else
165
+ hopDetails[middle] = [router, 255, "tstamp", false, router] if ($allhops or middle == tpath.path.length - 1);
166
+ end
167
+ else
168
+ hopDetails[middle] = [destination, ttl, "udp", false, router] if ($allhops or middle == tpath.path.length - 1);
169
+ end
170
+ end
171
+
172
+ middle+=1;
173
+ end
174
+ end
175
+
176
+
177
+ (1..hopDetails.length-1).map { |i| puts "hopdetails: #{tpath.path[i].hop}. #{hopDetails[i].join(" ")}" if (hopDetails[i])} if ($verbose>0);
178
+
179
+
180
+ if (pathology == "reordering") then Scriptroute::Tulip::ReorderingDoctor.new(hopDetails);
181
+ elsif (pathology == "loss") then Scriptroute::Tulip::LossDoctor.new(hopDetails);
182
+ else Scriptroute::Tulip::QueuingDoctor.new(hopDetails);
183
+ end
@@ -0,0 +1,327 @@
1
+ # @author Neil Spring
2
+
3
+ require 'socket'
4
+ require 'timeout'
5
+ require 'base64'
6
+ require 'resolv'
7
+ require 'scriptroute/packets'
8
+ require 'scriptroute/ally'
9
+ require 'scriptroute/rockettrace'
10
+ require 'scriptroute/nameify'
11
+
12
+ module Scriptroute
13
+
14
+ class ScriptrouteError < StandardError
15
+ end
16
+
17
+ # A ScriptrouteConnection is an object that wraps the TCP
18
+ # socket used to connect to a scriptroute daemon. It likely
19
+ # need not be used directly, unless accessing low level
20
+ # scriptroute commands not wrapped by the general
21
+ # Scriptroute module
22
+ class ScriptrouteConnection
23
+ Client_name = "%s(%d)" % [ defined?(Etc) ? Etc.getlogin : ENV['USER'], Process.uid ]
24
+ # this version is designed to emulate the 0.4.14 version of srinterpreter.
25
+ VERSION = '0.4.14'
26
+ def initialize
27
+ begin
28
+ timeout(5) do
29
+ @s = TCPSocket.new 'localhost', 3356
30
+ end
31
+ reply = ""
32
+ reply = one_line_command( "interpret v%s %s\n" % [ VERSION, Client_name ] )
33
+ if reply =~ /proceed( Hz=(\d+))/ then
34
+ # puts "negotiated."
35
+ else
36
+ puts "failed to negotiate: %s" % reply
37
+ end
38
+ rescue Errno::ECONNREFUSED => e
39
+ $stderr.puts "Connection refused connecting to scriptrouted, ensure that it is running"
40
+ raise e
41
+ end
42
+ end
43
+ # @param t [String] the command to issue, newline optional
44
+ # @return [String, nil] the one-line reply, or nil if the connection timed out or was terminated.
45
+ def one_line_command(t)
46
+ begin
47
+ reply = nil
48
+ timeout(2) do
49
+ issue_command t
50
+ reply = @s.readline
51
+ end
52
+ rescue EOFError
53
+ end
54
+ return reply
55
+ end
56
+ # @return [Hash] the configuration of the scriptroute daemon
57
+ def get_config
58
+ ret = Hash.new
59
+ timeout(5) do
60
+ issue_command "showconfig\n"
61
+ l = "do/while"
62
+ # showconfig ends reply with an empty line.
63
+ while l !~ /^#done/ && l !~ /^$/
64
+ l = @s.readline
65
+ if l =~ /^(\S+)\s+=\s+(.+)$/ then
66
+ ret[$1] = $2
67
+ elsif l =~ /^(\S+)\s+=\s*$/ then
68
+ ret[$1] = nil
69
+ elsif l =~ /^$/ then
70
+ # end.
71
+ else
72
+ puts "unparsed config: %s" % l
73
+ end
74
+ end
75
+ end
76
+ ret
77
+ end
78
+ # @param t [String] the command to issue, newline optional
79
+ def issue_command(t)
80
+ # $stderr.puts "writing %s" % t
81
+ if t[-1] == "\n" then
82
+ @s.write t
83
+ else
84
+ @s.write "%s\n" % [ t ]
85
+ end
86
+ end
87
+ # @return [String,nil] A one-line reply, or nil if the connection terminated.
88
+ def get_reply
89
+ begin
90
+ @s.readline
91
+ rescue EOFError
92
+ nil
93
+ end
94
+ end
95
+ end
96
+
97
+ # A DelayedPacket is for constructing trains in send_train.
98
+ Struct.new("DelayedPacket", :delay, :packet)
99
+
100
+ # TimedPacket is a time, packet tuple, with a tsc value thrown in in case its useful
101
+ class TimedPacket
102
+ # @return [Float,nil]
103
+ attr_accessor :time
104
+ # @return [Fixnum,nil] The output of rdtsc can be a more
105
+ # useful value in calculating rtt when NTP's adjustments
106
+ # via skew cause trouble.
107
+ attr_accessor :tsc
108
+ # @return [IPv4]
109
+ attr_accessor :packet
110
+
111
+ # @param time [Float,nil] Seconds since the epoch, or nil if we didn't see the packet leave due to pcap (happens)
112
+ # @param tsc [Fixnum,nil] Value of the cycle counter (rdtsc) or nil if not supported
113
+ # @param packet [IPv4] The packet received.
114
+ def initialize(time, tsc, packet)
115
+ raise ArgumentError, "no packet" unless packet
116
+ raise ArgumentError, "packet of the wrong class" unless packet.is_a?(IPv4)
117
+ @time = time
118
+ @tsc = tsc
119
+ @packet = packet
120
+ end
121
+ end
122
+
123
+ # A ProbeResponse is a pair of a probe and its response.
124
+ # Scriptroute is designed around the idea that a general
125
+ # purpose engine can recognize the response to any probe,
126
+ # and be in charge of doing so, so that measurement tools
127
+ # need not have the rights to look at every packet.
128
+ #
129
+ # This design does limit somewhat, since probes that are
130
+ # capable of soliciting more than one response (e.g., via
131
+ # fragmentation) will not be managed properly.
132
+ class ProbeResponse
133
+ # @return [TimedPacket]
134
+ attr_accessor :probe
135
+ # @return [TimedPacket]
136
+ attr_accessor :response
137
+ # @return [Float,nil] Provides the apparent round trip time of this probe-response pair, or nil if either time is missing.
138
+ def rtt
139
+ if response and probe and probe.time then
140
+ response.time - probe.time
141
+ else
142
+ nil
143
+ end
144
+ end
145
+ end
146
+ private
147
+
148
+ # Connection pool in case the script requires more than one connection to the server (for concurrent tests).
149
+ class ConnectionPool
150
+ @@connections_cache = []
151
+ # Connection pool mutex.
152
+ @@connections_mutex = Mutex.new
153
+ # Fetch from the connection pool or create a new connection
154
+ # @return [ScriptrouteConnection]
155
+ def ConnectionPool.get_idle_connection
156
+ @@connections_mutex.lock
157
+ if @@connections_cache.empty? then
158
+ @@connections_mutex.unlock
159
+ return ScriptrouteConnection.new
160
+ else
161
+ ret = @@connections_cache.shift
162
+ @@connections_mutex.unlock
163
+ return ret
164
+ end
165
+ end
166
+ # @param c [ScriptrouteConnection] the connection to return to the pool
167
+ # @return [void]
168
+ def ConnectionPool.return_idle_connection(c)
169
+ @@connections_mutex.synchronize {
170
+ @@connections_cache.push(c)
171
+ }
172
+ end
173
+ end
174
+ public
175
+ # Take a block to be executed with a ScriptrouteConnection
176
+ # from the pool. This is the function to use, since it
177
+ # manages the pooled connections explicitly. A leak in
178
+ # connections would be bad.
179
+ # @yield [ScriptrouteConnection] the connection for this block.
180
+ # @return the output of the block
181
+ def Scriptroute::with_scriptroute_connection
182
+ c = ConnectionPool.get_idle_connection
183
+ r = yield c
184
+ ConnectionPool.return_idle_connection(c)
185
+ r
186
+ end
187
+ # Check if the daemon is running by making a connection or
188
+ # checking the pool for an unused but working connection.
189
+ # @return [Boolean]
190
+ def Scriptroute::is_daemon_running?
191
+ begin
192
+ with_scriptroute_connection do |c|
193
+ end
194
+ return true
195
+ rescue Errno::ECONNREFUSED
196
+ return false
197
+ end
198
+ end
199
+ # Exit failure if the scriptroute daemon does not appear
200
+ # to be running. This is useful at the beginning of
201
+ # scripts that expect the daemon to run, to avoid adding
202
+ # extra error checking later.
203
+ # @return [true] if the script does ot exit because the daemon
204
+ # is not running.
205
+ def Scriptroute::daemon_running_or_exit
206
+ begin
207
+ with_scriptroute_connection do |c|
208
+ end
209
+ return true
210
+ rescue Errno::ECONNREFUSED
211
+ puts "Ensure that the scriptroute daemon is running and try again"
212
+ exit 1
213
+ end
214
+ end
215
+ # Query for the version of the daemon; useful if there's a
216
+ # feature only supported in a particular version. @return
217
+ # @return [String] the version string provided by of the running daemon
218
+ def Scriptroute::DaemonVersion
219
+ with_scriptroute_connection do |c|
220
+ puts c.one_line_command("version\n")
221
+ end
222
+ end
223
+ # @return [String] An IP address associated with the
224
+ # hostname. Currently just invokes Resolv.getaddress.
225
+ def Scriptroute::dns_lookup(name)
226
+ Resolv.getaddress name
227
+ end
228
+ # @return [Hash] the configuration of the running daemon,
229
+ # useful for checking rate limiting parameters or filters
230
+ # if a specially configured daemon is needed.
231
+ def Scriptroute::DaemonConfig
232
+ ret = nil
233
+ with_scriptroute_connection do |c|
234
+ ret = c.get_config
235
+ end
236
+ ret
237
+ end
238
+ # no op for backward compatibility with the srinterpreter version
239
+ # that had a dedicated packet instance
240
+ # @param s [String] A marshaled packet.
241
+ # @return [String] The input parameter, which is sufficient for this implementation of {Scriptroute::send_train}.
242
+ def Scriptroute::pkt_from_string(s)
243
+ s
244
+ end
245
+ # This is the nut.
246
+ # @param train [Array<Struct::DelayedPacket,Array>] an array of Struct::DelayedPacket entries or arrays containing delay, packet pairs.
247
+ # @return [Array<Scriptroute::ProbeResponse>] an array of Scriptroute::ProbeResponse entries, comprising a probe and response.
248
+ def Scriptroute::send_train(train)
249
+ ret = nil
250
+ return [] if train.empty? # easy?
251
+ with_scriptroute_connection do |c|
252
+ # issue the send train command.
253
+ i = 1;
254
+ total_delay = 0
255
+ train.map! { |boxcar|
256
+ raise ArgumentError, 'nil entry' unless boxcar
257
+ if boxcar.is_a?(Array) then
258
+ Struct::DelayedPacket.new(*boxcar)
259
+ else
260
+ raise ArgumentError, 'not a boxcar or an array' unless boxcar.is_a?(Struct::DelayedPacket)
261
+ boxcar
262
+ end
263
+ }
264
+ train.each { |boxcar|
265
+ # needs validation.
266
+ delay = boxcar[:delay]
267
+ packet = boxcar[:packet]
268
+ raise "no packet" unless packet
269
+ if packet.is_a?(Scriptroute::IPv4) then
270
+ packet = packet.marshal
271
+ end
272
+ raise "packet is #{packet.class}, not a string" unless packet.is_a?(String)
273
+ encoded_packet = Base64.strict_encode64(packet) # strict_encode means no line feeds added.
274
+ c.issue_command "sendtrain %d/%d %f %d %s\n" % [ i, train.length, delay<0 ? 0 : delay, encoded_packet.length, encoded_packet ]
275
+ i+=1
276
+ total_delay += delay
277
+ }
278
+
279
+ # allocate space for the responses, array of probe response pairs, each of which will be a time/packet pair.
280
+ ret = Array.new(i-1) { ProbeResponse.new }
281
+
282
+ # collect, waiting maybe 5 minutes more than needed just in case the daemon hangs on us.
283
+ begin
284
+ timeout(total_delay + 300) do
285
+ while l = c.get_reply and l !~ /^done/ and l =~ /\S+/ do
286
+ if l =~ /^ERROR/ then
287
+ # have to throw an exception, although individual packet errors are possible, that's
288
+ # not communicated by the daemon.
289
+ $stderr.puts l
290
+ if l=~ /disjoint train \missing packet #(\d+)/ then
291
+ ret[ $1.to_i ].probe = nil
292
+ ret[ $1.to_i ].response = nil
293
+ end
294
+ raise ScriptrouteError, l
295
+ else
296
+ if l =~ /^(-?\d+\.\d+)\/(\d+) (\d+)([<>]) \d+ (\S+)$/ then
297
+ tv_s = $1.to_f
298
+ rdtsc = $2
299
+ pk = $3.to_i - 1
300
+ io = ($4 == '>') ? 0 : 1
301
+ st = $5
302
+ packet_string = Base64.strict_decode64(st)
303
+ p = IPv4.creator(packet_string)
304
+ tv_s = nil if tv_s < 0 # didn't see it leave.
305
+ tp = TimedPacket.new(tv_s, rdtsc, p)
306
+ if io == 0 then
307
+ ret[ pk ].probe = tp
308
+ else
309
+ ret[ pk ].response = tp
310
+ end
311
+ else
312
+ puts "unparsed send_train response: #{l}"
313
+ end
314
+ end
315
+ end # while
316
+ end # timeout
317
+ rescue TimeoutError
318
+ $stderr.puts "timed out parsing responses for send_train"
319
+ end
320
+ end
321
+ return ret
322
+ end
323
+ end
324
+
325
+ # Local Variables:
326
+ # compile-command: "rake test"
327
+ # End: