scriptroute 0.4.14 → 0.4.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/sr-traceroute6 +37 -0
- data/lib/scriptroute.rb +13 -8
- data/lib/scriptroute/ally.rb +1 -1
- data/lib/scriptroute/ipaddr4.rb +58 -0
- data/lib/scriptroute/packets.rb +243 -26
- data/test/test_commando.rb +26 -0
- data/test/test_scriptroute.rb +167 -11
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e286d4b4fa4a450c8a2aaac1fe6987ae1ac8ed8b
|
4
|
+
data.tar.gz: af98fdafbc90ad8b9b528b0104851f3e5465e88e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab994b7c31ffdc3b72622689776bd70cd3545bd8abfe6c046112130cbae57874773b3c28b399206e1fbf59079bd1cefd1573bcaa5eee244f2f88d6826884a5f0
|
7
|
+
data.tar.gz: f8f93fc4ef3c998d1a622e476ff1a414bce2fd3ace0720ac7a83f8e2ad44c8bb006d895c7aeced6cad670eeeea07f8c0eca8a534827d5404e5465199845a8ff9
|
data/bin/sr-traceroute6
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'scriptroute'
|
4
|
+
require 'scriptroute/packets'
|
5
|
+
|
6
|
+
if ARGV.empty? then
|
7
|
+
puts "usage: #{$0} address_or_hostname"
|
8
|
+
exit 1
|
9
|
+
end
|
10
|
+
|
11
|
+
probe = Scriptroute::UDP6.new(12)
|
12
|
+
|
13
|
+
probe.ip6_dst = ARGV[0]
|
14
|
+
|
15
|
+
unreach = false
|
16
|
+
|
17
|
+
puts "Traceroute to #{ARGV[0]} (#{probe.ip6_dst})"
|
18
|
+
|
19
|
+
catch(:unreachable) do
|
20
|
+
( 1..64 ).each { |ttl|
|
21
|
+
( 1..3 ).each { |rep|
|
22
|
+
probe.ip_ttl = ttl
|
23
|
+
packets = Scriptroute::send_train([ Struct::DelayedPacket.new(0,probe) ])
|
24
|
+
response = (packets[0].response) ? packets[0].response.packet : nil
|
25
|
+
if(response) then
|
26
|
+
puts '%d %s %5.3f ms' % [ ttl, response.ip6_src, packets[0].rtt * 1000.0 ]
|
27
|
+
if(response.is_a?(Scriptroute::ICMP6)) then
|
28
|
+
unreach = true if(response.icmp_type == Scriptroute::ICMP6::ICMP6_DST_UNREACH)
|
29
|
+
end
|
30
|
+
else
|
31
|
+
puts ttl.to_s + ' *'
|
32
|
+
end
|
33
|
+
$stdout.flush
|
34
|
+
}
|
35
|
+
throw :unreachable if(unreach)
|
36
|
+
}
|
37
|
+
end
|
data/lib/scriptroute.rb
CHANGED
@@ -105,15 +105,15 @@ end
|
|
105
105
|
# useful value in calculating rtt when NTP's adjustments
|
106
106
|
# via skew cause trouble.
|
107
107
|
attr_accessor :tsc
|
108
|
-
# @return [
|
108
|
+
# @return [IP]
|
109
109
|
attr_accessor :packet
|
110
110
|
|
111
111
|
# @param time [Float,nil] Seconds since the epoch, or nil if we didn't see the packet leave due to pcap (happens)
|
112
112
|
# @param tsc [Fixnum,nil] Value of the cycle counter (rdtsc) or nil if not supported
|
113
|
-
# @param packet [
|
113
|
+
# @param packet [IP] The packet received.
|
114
114
|
def initialize(time, tsc, packet)
|
115
115
|
raise ArgumentError, "no packet" unless packet
|
116
|
-
raise ArgumentError, "packet of the wrong class" unless packet.is_a?(
|
116
|
+
raise ArgumentError, "packet of the wrong class" unless packet.is_a?(IP)
|
117
117
|
@time = time
|
118
118
|
@tsc = tsc
|
119
119
|
@packet = packet
|
@@ -225,15 +225,20 @@ end
|
|
225
225
|
def Scriptroute::dns_lookup(name)
|
226
226
|
Resolv.getaddress name
|
227
227
|
end
|
228
|
+
private
|
229
|
+
@@config = nil
|
230
|
+
public
|
228
231
|
# @return [Hash] the configuration of the running daemon,
|
229
232
|
# useful for checking rate limiting parameters or filters
|
230
233
|
# if a specially configured daemon is needed.
|
231
234
|
def Scriptroute::DaemonConfig
|
232
235
|
ret = nil
|
233
|
-
|
234
|
-
|
236
|
+
unless @@config then
|
237
|
+
with_scriptroute_connection do |c|
|
238
|
+
@@config = c.get_config
|
239
|
+
end
|
235
240
|
end
|
236
|
-
|
241
|
+
@@config
|
237
242
|
end
|
238
243
|
# no op for backward compatibility with the srinterpreter version
|
239
244
|
# that had a dedicated packet instance
|
@@ -266,7 +271,7 @@ end
|
|
266
271
|
delay = boxcar[:delay]
|
267
272
|
packet = boxcar[:packet]
|
268
273
|
raise "no packet" unless packet
|
269
|
-
if packet.is_a?(Scriptroute::
|
274
|
+
if packet.is_a?(Scriptroute::IP) then
|
270
275
|
packet = packet.marshal
|
271
276
|
end
|
272
277
|
raise "packet is #{packet.class}, not a string" unless packet.is_a?(String)
|
@@ -300,7 +305,7 @@ end
|
|
300
305
|
io = ($4 == '>') ? 0 : 1
|
301
306
|
st = $5
|
302
307
|
packet_string = Base64.strict_decode64(st)
|
303
|
-
p =
|
308
|
+
p = IP.creator(packet_string)
|
304
309
|
tv_s = nil if tv_s < 0 # didn't see it leave.
|
305
310
|
tp = TimedPacket.new(tv_s, rdtsc, p)
|
306
311
|
if io == 0 then
|
data/lib/scriptroute/ally.rb
CHANGED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
# require 'my-ipaddr'
|
3
|
+
|
4
|
+
class IPAddr4 < IPAddr
|
5
|
+
# subclass that trusts its initializer and assumes IPv4
|
6
|
+
def in_addr(addr)
|
7
|
+
if addr =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
|
8
|
+
(($1.to_i << 24) + ($2.to_i << 16) + ($3.to_i << 8) + ($4.to_i))
|
9
|
+
else
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
def initialize(addr)
|
14
|
+
@family = Socket::AF_INET
|
15
|
+
if( addr.kind_of?(String) ) then
|
16
|
+
prefix, len = addr.split('/')
|
17
|
+
@addr = in_addr(prefix)
|
18
|
+
if(len) then
|
19
|
+
mask_len!(len.to_i)
|
20
|
+
else
|
21
|
+
@mask_addr = IN4MASK
|
22
|
+
end
|
23
|
+
else
|
24
|
+
@addr = addr
|
25
|
+
@mask_addr = IN4MASK
|
26
|
+
end
|
27
|
+
end
|
28
|
+
# faster include method that only works for ipv4.
|
29
|
+
def include?(other)
|
30
|
+
if other.kind_of?(IPAddr)
|
31
|
+
other_addr = other.to_i
|
32
|
+
else # Not IPAddr - assume integer in same family as us
|
33
|
+
other_addr = other.to_i
|
34
|
+
end
|
35
|
+
return ((@addr & @mask_addr) == (other_addr & @mask_addr))
|
36
|
+
end
|
37
|
+
def eql?(other)
|
38
|
+
if other.kind_of?(IPAddr4) && @family != other.family
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
return (@addr == other.to_i)
|
42
|
+
end
|
43
|
+
def hash
|
44
|
+
@addr.to_i
|
45
|
+
end
|
46
|
+
def to_s
|
47
|
+
# about twice as fast as the map/join based to_s.
|
48
|
+
# appears slightly faster than mask then shift.
|
49
|
+
"%d.%d.%d.%d" % [ (@addr >> 24) & 0xff, (@addr >> 16) & 0xff, (@addr >> 8) & 0xff, (0xff&@addr) ]
|
50
|
+
end
|
51
|
+
def ==(other)
|
52
|
+
if other.kind_of?(IPAddr4)
|
53
|
+
@addr == other.addr
|
54
|
+
else
|
55
|
+
@addr == other.to_i # dunno why.
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/scriptroute/packets.rb
CHANGED
@@ -2,10 +2,140 @@ module Scriptroute
|
|
2
2
|
# a small library of routines for constructing packets
|
3
3
|
# as strings to hand off to the interpreter.
|
4
4
|
|
5
|
-
class
|
6
|
-
|
7
|
-
IPPROTO_TCP = 6
|
5
|
+
class IP
|
6
|
+
IPPROTO_TCP = 6 # apply to both 4 and 6.
|
8
7
|
IPPROTO_UDP = 17
|
8
|
+
def to_bytes
|
9
|
+
marshal
|
10
|
+
end
|
11
|
+
def IP.creator(str)
|
12
|
+
ip_vhl = str.unpack("c")[0]
|
13
|
+
ip_v = ((ip_vhl & 0xf0) >> 4)
|
14
|
+
if(ip_v == 4) then
|
15
|
+
IPv4.creator(str)
|
16
|
+
elsif(ip_v == 6) then
|
17
|
+
IPv6.creator(str)
|
18
|
+
else
|
19
|
+
raise "unknown IP version #%d in %s" % [ ip_v, str.unpack("C*").map { |c| "%x" % c }.join(' ') ]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class IPv4 < IP
|
25
|
+
IPPROTO_ICMP = 1
|
26
|
+
end
|
27
|
+
|
28
|
+
class IPv6 < IP
|
29
|
+
IPPROTO_ICMP6 = 58
|
30
|
+
attr_accessor :ip6_src, :ip6_hlim, :ip6_flow
|
31
|
+
attr_reader :ip6_dst
|
32
|
+
@@creators = Hash.new
|
33
|
+
def IPv6.creator(str)
|
34
|
+
flow, plen, nxt, hlim, saddr, daddr = str.unpack("Nncca16a16")
|
35
|
+
if(@@creators[nxt]) then
|
36
|
+
pkt = (@@creators[nxt]).call(str[40..40+plen])
|
37
|
+
pkt.ipv6_unmarshal(str)
|
38
|
+
pkt
|
39
|
+
else
|
40
|
+
raise "unknown IPv6 next header #%d in %s" % [ nxt, str.unpack("H*") ]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
def ipv6_unmarshal(str)
|
44
|
+
@ip6_flow, @ip6_plen, @ip6_nxt, @ip6_hlim, ip6_saddr, ip6_daddr = str.unpack("Nncca16a16")
|
45
|
+
@ip6_src, @ip6_dst = [ip6_saddr, ip6_daddr].map { |addr| IPAddr.new_ntoh(addr) }
|
46
|
+
end
|
47
|
+
def initialize(p)
|
48
|
+
if(p.is_a?(Fixnum)) then
|
49
|
+
@ip6_src = @ip6_dst = @ip6flow = nil
|
50
|
+
@ip6_hlim = 64
|
51
|
+
@ip6_nxt = p
|
52
|
+
# actually, unlikely 7 or 13 are at the moment
|
53
|
+
# either, but let's start here.
|
54
|
+
$stderr.puts "unlikely v6 protocol header type #{p} is supported" unless [ 7, 13, 58 ].include?(p)
|
55
|
+
else
|
56
|
+
raise "need a next header type for constructing a v6 packet"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
# for simplicity.
|
60
|
+
def ip_ttl=(ttl)
|
61
|
+
@ip6_hlim = ttl
|
62
|
+
end
|
63
|
+
def ip6_dst=(name_or_address)
|
64
|
+
if name_or_address.is_a?(IPAddr) then
|
65
|
+
if name_or_address.ipv4? then
|
66
|
+
@ip6_dst = name_or_address.ipv4_mapped # try to use it.
|
67
|
+
else
|
68
|
+
@ip6_dst = name_or_address
|
69
|
+
end
|
70
|
+
elsif Resolv::IPv6::Regex =~ name_or_address then
|
71
|
+
@ip6_dst = IPAddr.new(name_or_address, Socket::AF_INET6)
|
72
|
+
else
|
73
|
+
r = Resolv::DNS.new.getresource(name_or_address, Resolv::DNS::Resource::IN::AAAA)
|
74
|
+
if r then
|
75
|
+
@ip6_dst = IPAddr.new(r.address.to_s, Socket::AF_INET6)
|
76
|
+
else
|
77
|
+
raise "failed to lookup #{name_or_address} into a v6 address"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
def marshal
|
82
|
+
# probably should force src/dst internal
|
83
|
+
# representation to be IPAddr via accessor methods
|
84
|
+
# rather than reconvert here.
|
85
|
+
puts "unlikely you wanted zero hlim" unless @ip6_hlim and @ip6_hlim > 0
|
86
|
+
puts "unlikely you wanted no next protocol" unless @ip6_nxt and @ip6_nxt > 0
|
87
|
+
puts "unlikely you wanted nil payload len" unless ip_payload_len
|
88
|
+
v6packet = [ 6 << 4, 0, 0, ip_payload_len, @ip6_nxt, @ip6_hlim,
|
89
|
+
IPAddr.new(@ip6_src, Socket::AF_INET6).hton,
|
90
|
+
IPAddr.new(@ip6_dst, Socket::AF_INET6).hton ].pack("ccnncca16a16")
|
91
|
+
end
|
92
|
+
def to_s
|
93
|
+
"%s -> %s hlim=%d" % [ (@ip6_src or "::0"), (@ip6_dst or "::0"), @ip6_hlim ]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
class ICMP6 < IPv6
|
97
|
+
attr_reader :icmp_type, :icmp_code, :icmp_cksum
|
98
|
+
attr_reader :ip_payload_len
|
99
|
+
# type
|
100
|
+
ICMP6_ECHO = 128
|
101
|
+
ICMP6_ECHOREPLY = 129
|
102
|
+
ICMP6_DST_UNREACH = 1
|
103
|
+
ICMP6_PACKET_TOO_BIG = 2
|
104
|
+
ICMP6_TIME_EXCEEDED = 3
|
105
|
+
ICMP6_PARAM_PROB = 4
|
106
|
+
|
107
|
+
@@icmp6_creators = Hash.new
|
108
|
+
@@creators[IPPROTO_ICMP6] = lambda { |hdr|
|
109
|
+
icmp_type, icmp_code, icmp_cksum = hdr.unpack("CCn")
|
110
|
+
if(@@icmp6_creators[icmp_type]) then
|
111
|
+
pkt = @@icmp6_creators[icmp_type].call(hdr)
|
112
|
+
else
|
113
|
+
raise "unknown icmp6 type #%d" % icmp_type
|
114
|
+
end
|
115
|
+
}
|
116
|
+
def initialize(type_or_str)
|
117
|
+
if(type_or_str.is_a?(Fixnum)) then
|
118
|
+
@icmp_type = type_or_str
|
119
|
+
@icmp_code = 0
|
120
|
+
@icmp_cksum = 0
|
121
|
+
super(IPPROTO_ICMP6)
|
122
|
+
else
|
123
|
+
@icmp_type, @icmp_code, @icmp_cksum = type_or_str.unpack("CCn")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
# @return [String] The packet in string form
|
127
|
+
def marshal
|
128
|
+
@icmp_type or raise "type is nil"
|
129
|
+
@icmp_code or raise "code is nil"
|
130
|
+
@icmp_cksum = 0
|
131
|
+
super + [ @icmp_type, @icmp_code, @icmp_cksum ].pack("CCn")
|
132
|
+
end
|
133
|
+
# @return [String]
|
134
|
+
def to_s
|
135
|
+
super + ": ICMP6: type %d code %d cksum %d" %[ @icmp_type, @icmp_code, @icmp_cksum ]
|
136
|
+
end
|
137
|
+
#instantiate echo instead.
|
138
|
+
private :marshal
|
9
139
|
end
|
10
140
|
|
11
141
|
class ICMP < IPv4
|
@@ -227,14 +357,22 @@ module Scriptroute
|
|
227
357
|
ip_id, ip_off,
|
228
358
|
ip_ttl, ip_p, ip_sum,
|
229
359
|
ip_src, ip_dst = str.unpack("ccn" + "nn" + "ccn" + "N" + "N");
|
230
|
-
|
231
|
-
if(
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
360
|
+
ip_v = ((ip_vhl & 0xf0) >> 4)
|
361
|
+
if(ip_v == 4) then
|
362
|
+
ip_hl = ip_vhl & 0xf;
|
363
|
+
if(@@creators[ip_p]) then
|
364
|
+
pkt = (@@creators[ip_p]).call(str[(ip_hl * 4) .. ip_len])
|
365
|
+
pkt.ipv4_unmarshal(str)
|
366
|
+
pkt
|
367
|
+
else
|
368
|
+
raise "unknown IPv4 protocol #%d in %s" % [ ip_p, str.unpack("C*").map { |c| "%x" % c }.join(' ') ]
|
369
|
+
end
|
370
|
+
elsif(ip_v == 6) then
|
371
|
+
# probably should do this someplace above IPv4 creator.
|
372
|
+
puts "received v6 packet %s" % [ str.unpack("H*") ]
|
373
|
+
str
|
236
374
|
else
|
237
|
-
raise "unknown
|
375
|
+
raise "unknown IP version #%d in %s" % [ ip_v, str.unpack("C*").map { |c| "%x" % c }.join(' ') ]
|
238
376
|
end
|
239
377
|
end
|
240
378
|
|
@@ -398,24 +536,14 @@ module Scriptroute
|
|
398
536
|
end
|
399
537
|
end
|
400
538
|
|
401
|
-
|
539
|
+
module UDPgeneric
|
402
540
|
# @return [Fixnum]
|
403
541
|
attr_reader :uh_sport, :uh_dport, :uh_ulen, :uh_sum
|
404
542
|
attr_writer :uh_dport, :uh_sum
|
405
543
|
|
406
|
-
@@creators[IPPROTO_UDP] = lambda { |hdr|
|
407
|
-
uh_sport, uh_dport, uh_ulen, uh_sum = hdr.unpack("nnnn")
|
408
|
-
if uh_sport==123 || uh_dport==123 then
|
409
|
-
p = NTP.new(hdr[8..hdr.length])
|
410
|
-
p.udp_unmarshal(hdr)
|
411
|
-
else
|
412
|
-
p = UDP.new(hdr)
|
413
|
-
end
|
414
|
-
p
|
415
|
-
}
|
416
|
-
|
417
544
|
# @return [Fixnum] the udp data plus header length, uh_ulen
|
418
545
|
def ip_payload_len
|
546
|
+
raise "ip_payload_len was nil when asked" unless @uh_ulen
|
419
547
|
@uh_ulen
|
420
548
|
end
|
421
549
|
|
@@ -431,7 +559,7 @@ module Scriptroute
|
|
431
559
|
@uh_sport = 32945
|
432
560
|
@uh_dport = 33434
|
433
561
|
@uh_sum = 0
|
434
|
-
super( IPPROTO_UDP )
|
562
|
+
super( Scriptroute::IP::IPPROTO_UDP )
|
435
563
|
else
|
436
564
|
@uh_sport, @uh_dport, @uh_ulen, @uh_sum = paylen_or_str.unpack("nnnn")
|
437
565
|
end
|
@@ -445,12 +573,11 @@ module Scriptroute
|
|
445
573
|
array_of_elements = [ @uh_sport, @uh_dport, @uh_ulen, @uh_sum ]
|
446
574
|
raise "a UDP header field was unset" if array_of_elements.include?(nil)
|
447
575
|
super + [ @uh_sport, @uh_dport, @uh_ulen, @uh_sum ].pack("nnnn") +
|
448
|
-
if ( self.class == UDP ) then
|
576
|
+
if ( self.class == UDP or self.class == UDP6 ) then
|
449
577
|
"\0" * ( @uh_ulen - 8 )
|
450
578
|
else
|
451
579
|
"" # the subclass will take care of it
|
452
580
|
end
|
453
|
-
|
454
581
|
end
|
455
582
|
|
456
583
|
# Used for subclasses to set the UDP header fields.
|
@@ -468,6 +595,29 @@ module Scriptroute
|
|
468
595
|
|
469
596
|
end
|
470
597
|
|
598
|
+
class UDP < IPv4
|
599
|
+
include UDPgeneric
|
600
|
+
|
601
|
+
@@creators[IPPROTO_UDP] = lambda { |hdr|
|
602
|
+
uh_sport, uh_dport, uh_ulen, uh_sum = hdr.unpack("nnnn")
|
603
|
+
if uh_sport==123 || uh_dport==123 then
|
604
|
+
p = NTP.new(hdr[8..hdr.length])
|
605
|
+
p.udp_unmarshal(hdr)
|
606
|
+
else
|
607
|
+
p = UDP.new(hdr)
|
608
|
+
end
|
609
|
+
p
|
610
|
+
}
|
611
|
+
end
|
612
|
+
|
613
|
+
class UDP6 < IPv6
|
614
|
+
include UDPgeneric
|
615
|
+
|
616
|
+
@@creators[IPPROTO_UDP] = lambda { |hdr|
|
617
|
+
UDP6.new(hdr)
|
618
|
+
}
|
619
|
+
end
|
620
|
+
|
471
621
|
class TCP < IPv4
|
472
622
|
attr_reader :th_sport, :th_dport, :th_sum, :th_seq, :th_ack,
|
473
623
|
:th_win, :th_flags, :th_win, :th_sum, :th_urp
|
@@ -584,6 +734,38 @@ module Scriptroute
|
|
584
734
|
super + ": ECHO: id %d seq %d len %d" % [ @icmp_id, @icmp_seq, @ip_payload_len ]
|
585
735
|
end
|
586
736
|
end
|
737
|
+
# Class for ping packets (echo and echo reply) much duplicated with ICMPecho
|
738
|
+
class ICMP6echo < ICMP6
|
739
|
+
# @return [Fixnum]
|
740
|
+
attr_reader :icmp_id, :icmp_seq
|
741
|
+
attr_writer :icmp_seq
|
742
|
+
@@icmp6_creators[ICMP6_ECHO] =
|
743
|
+
@@icmp6_creators[ICMP6_ECHOREPLY] = lambda { |hdr|
|
744
|
+
ICMP6echo.new(hdr)
|
745
|
+
}
|
746
|
+
def initialize(paylen_or_str = 0)
|
747
|
+
if(paylen_or_str.is_a?(Fixnum)) then
|
748
|
+
if( paylen_or_str < 0) then raise "payload length must be >= 0" end
|
749
|
+
@ip_payload_len = paylen_or_str + 4 + 4
|
750
|
+
@icmp_id = 666
|
751
|
+
@icmp_seq = 1
|
752
|
+
super(ICMP6_ECHO)
|
753
|
+
else
|
754
|
+
# x is skip forward a character.
|
755
|
+
@ip_payload_len = paylen_or_str.length - 8
|
756
|
+
@icmp_id, @icmp_seq = paylen_or_str.unpack("xxxxnn")
|
757
|
+
super(paylen_or_str)
|
758
|
+
end
|
759
|
+
end
|
760
|
+
# @return [String] The packet in string form
|
761
|
+
def marshal
|
762
|
+
super + [ @icmp_id, @icmp_seq ].pack("nn") + "\0" * ( @ip_payload_len - 4 - 4 )
|
763
|
+
end
|
764
|
+
# @return [String]
|
765
|
+
def to_s
|
766
|
+
super + ": ECHO6: id %d seq %d len %d" % [ @icmp_id, @icmp_seq, @ip_payload_len ]
|
767
|
+
end
|
768
|
+
end
|
587
769
|
|
588
770
|
class ICMPtstamp < ICMP
|
589
771
|
# @return [Fixnum]
|
@@ -666,6 +848,33 @@ module Scriptroute
|
|
666
848
|
end
|
667
849
|
end
|
668
850
|
|
851
|
+
class ICMP6unreach< ICMP6
|
852
|
+
# @return [IPv4] The packet header embedded within the ICMP unreachable error message.
|
853
|
+
attr_reader :contents
|
854
|
+
@@icmp6_creators[ICMP6_DST_UNREACH] = @@icmp6_creators[ICMP6_TIME_EXCEEDED] =
|
855
|
+
lambda { |hdr|
|
856
|
+
ICMP6unreach.new(hdr)
|
857
|
+
}
|
858
|
+
# Can create an unreachable only from string contents, never from filling in fields given a size.
|
859
|
+
# param string [String] the contents of the received packet.
|
860
|
+
def initialize(string)
|
861
|
+
# first four are code, type, checksum.
|
862
|
+
# second four are undefined
|
863
|
+
@contents = IPv6.creator(string[8..-1])
|
864
|
+
super(string)
|
865
|
+
end
|
866
|
+
# Cannot marshal an unreachable packet for transmission; raises an exception.
|
867
|
+
# @return [void]
|
868
|
+
def marshal
|
869
|
+
raise "not supported"
|
870
|
+
end
|
871
|
+
# @return [String] formats the packet and the embedded packet as a string.
|
872
|
+
def to_s
|
873
|
+
super + " ( " + @contents.to_s + " )"
|
874
|
+
end
|
875
|
+
end
|
876
|
+
|
877
|
+
|
669
878
|
class NTP < UDP
|
670
879
|
# @return [Fixnum]
|
671
880
|
attr_accessor :leap_indicator, :version_number, :mode, :stratum
|
@@ -790,7 +999,15 @@ module Scriptroute
|
|
790
999
|
# this method implemented in pure ruby regardless of interpreter state.
|
791
1000
|
# @return [String]
|
792
1001
|
def to_s
|
793
|
-
|
1002
|
+
if @probe then
|
1003
|
+
if(@probe.time) then
|
1004
|
+
"%s @%5.6f -> %s +%5.6f" % [@probe.packet, @probe.time, (@response ? @response.packet : "<none>"), (rtt or "-1")]
|
1005
|
+
else
|
1006
|
+
"%s (not seen leaving) -> %s" % [@probe.packet, (@response or "<none>")]
|
1007
|
+
end
|
1008
|
+
else
|
1009
|
+
"bizarre nothingness. no probe."
|
1010
|
+
end
|
794
1011
|
end
|
795
1012
|
end
|
796
1013
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'scriptroute/commando'
|
5
|
+
|
6
|
+
$SAFE = 2
|
7
|
+
|
8
|
+
|
9
|
+
class ScriptrouteCommandoTest < Test::Unit::TestCase
|
10
|
+
|
11
|
+
def test_one
|
12
|
+
|
13
|
+
c = Commando.new([ "--start-speed", 3 ],
|
14
|
+
[ CommandoVar.new( "--start-speed", "Kbps rate to start" , "$StartSpeedKbps", 20),
|
15
|
+
CommandoVar.new( "--train-length", "Filler packets in train" , "$TrainLength", 20),
|
16
|
+
CommandoVar.new( "--hop", "Specific hop to study (ttl=hop and hop-1), 0 for e2e", "$Hop", 0 ),
|
17
|
+
CommandoVar.new( [ "--verbose", "-v" ], "print gobs of messages" , "$VERBOSE", false) ],
|
18
|
+
"destination-host")
|
19
|
+
|
20
|
+
raise "failed to set dollar startspeedkbps" unless($StartSpeedKbps == 3)
|
21
|
+
raise "failed to default dollar TrainLength" unless($TrainLength == 20)
|
22
|
+
raise "failed to construct Commando c" unless c
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
data/test/test_scriptroute.rb
CHANGED
@@ -2,10 +2,11 @@
|
|
2
2
|
require 'test/unit'
|
3
3
|
require 'scriptroute'
|
4
4
|
require 'scriptroute/nameify'
|
5
|
+
require 'ipaddr'
|
5
6
|
|
6
7
|
class ScriptrouteTest < Test::Unit::TestCase
|
7
8
|
def test_abba # be first
|
8
|
-
unless Scriptroute
|
9
|
+
unless Scriptroute.is_daemon_running?
|
9
10
|
exit
|
10
11
|
end
|
11
12
|
|
@@ -21,8 +22,8 @@ class ScriptrouteTest < Test::Unit::TestCase
|
|
21
22
|
end
|
22
23
|
def test_abc_get_config
|
23
24
|
raise "no config returned" unless Scriptroute::DaemonConfig()
|
24
|
-
raise "config not a hash" unless Scriptroute::DaemonConfig.is_a?(Hash)
|
25
|
-
raise "config lacks an expected key" unless Scriptroute::DaemonConfig
|
25
|
+
raise "config not a hash" unless Scriptroute::DaemonConfig().is_a?(Hash)
|
26
|
+
raise "config lacks an expected key" unless Scriptroute::DaemonConfig().has_key?('Filter.listen_filter')
|
26
27
|
end
|
27
28
|
def test_ally
|
28
29
|
verdict = Scriptroute::Ally.new('128.8.126.92', '128.8.126.104')
|
@@ -34,6 +35,7 @@ class ScriptrouteTest < Test::Unit::TestCase
|
|
34
35
|
end
|
35
36
|
|
36
37
|
def test_forbidden
|
38
|
+
$stderr.puts "the next line should be 'ERROR: packet administratively filtered'..."
|
37
39
|
i = Scriptroute::ICMPecho.new(0)
|
38
40
|
# an address ending with .255 seems more likely to be broadcast.
|
39
41
|
i.ip_dst = 0x805f02ff
|
@@ -87,31 +89,35 @@ class ScriptrouteTest < Test::Unit::TestCase
|
|
87
89
|
puts m.length.to_s + " " + m.gsub(/./) { |b| "%x " % b[0].to_i }
|
88
90
|
puts m.length.to_s + " " + m.bytes.map { |b| "%x " % b }.join if has_bytes ## 1.9
|
89
91
|
# puts "decode: " + Scriptroute::stringpacket(m)
|
90
|
-
|
92
|
+
q = Scriptroute::pkt_from_string(m)
|
91
93
|
|
92
94
|
m = Scriptroute::ICMPtstamp.new.marshal
|
93
95
|
puts m.length.to_s + " " + m.gsub(/./) { |b| "%x " % b[0].to_i }
|
94
96
|
puts m.length.to_s + " " + m.bytes.map { |b| "%x " % b }.join if has_bytes ## 1.9
|
95
97
|
# puts "decode: " + Scriptroute::stringpacket(m)
|
96
|
-
|
98
|
+
q = Scriptroute::pkt_from_string(m)
|
99
|
+
# garbage. puts "encode: " + Scriptroute::pkt_from_string(m).to_s
|
97
100
|
|
98
101
|
m = Scriptroute::TCP.new.marshal
|
99
102
|
puts m.length.to_s + " " + m.gsub(/./) { |b| "%x " % b[0].to_i }
|
100
103
|
puts m.length.to_s + " " + m.bytes.map { |b| "%x " % b }.join if has_bytes ## 1.9
|
101
104
|
# puts "decode: " + Scriptroute::stringpacket(m)
|
102
|
-
puts "encode: " + Scriptroute::pkt_from_string(m).to_s
|
105
|
+
# garbage. puts "encode: " + Scriptroute::pkt_from_string(m).to_s
|
106
|
+
q = Scriptroute::pkt_from_string(m)
|
103
107
|
|
104
108
|
m = Scriptroute::UDP.new.marshal
|
105
109
|
puts m.length.to_s + " " + m.gsub(/./) { |b| "%x " % b[0].to_i }
|
106
110
|
puts m.length.to_s + " " + m.bytes.map { |b| "%x " % b }.join if has_bytes ## 1.9
|
107
111
|
# puts "decode: " + Scriptroute::stringpacket(m)
|
108
|
-
puts "encode: " + Scriptroute::pkt_from_string(m).to_s
|
112
|
+
# garbage. puts "encode: " + Scriptroute::pkt_from_string(m).to_s
|
113
|
+
q = Scriptroute::pkt_from_string(m)
|
109
114
|
|
110
115
|
m = Scriptroute::UDP.new(8).marshal
|
111
116
|
puts m.length.to_s + " " + m.gsub(/./) { |b| "%x " % b[0].to_i }
|
112
117
|
puts m.length.to_s + " " + m.bytes.map { |b| "%x " % b }.join if has_bytes ## 1.9
|
113
|
-
puts "decode: " + Scriptroute::IPv4.creator(m).to_s
|
114
|
-
|
118
|
+
# garbage. puts "decode: " + Scriptroute::IPv4.creator(m).to_s
|
119
|
+
q = Scriptroute::pkt_from_string(m)
|
120
|
+
# garbage. puts "encode: " + Scriptroute::pkt_from_string(m).to_s
|
115
121
|
|
116
122
|
t = Scriptroute::TCP.new
|
117
123
|
opt = Scriptroute::Timestamp_option.new(1)
|
@@ -125,7 +131,8 @@ class ScriptrouteTest < Test::Unit::TestCase
|
|
125
131
|
puts m.length.to_s + " " + m.gsub(/./) { |b| "%x " % b[0].to_i }
|
126
132
|
puts m.length.to_s + " " + m.bytes.map { |b| "%x " % b }.join if has_bytes ## 1.9
|
127
133
|
# puts "decode: " + Scriptroute::stringpacket(m)
|
128
|
-
puts "encode: " + Scriptroute::pkt_from_string(m).to_s
|
134
|
+
# garbage. puts "encode: " + Scriptroute::pkt_from_string(m).to_s
|
135
|
+
q = Scriptroute::pkt_from_string(m)
|
129
136
|
|
130
137
|
#1.times { |i|
|
131
138
|
# m=t.marshal
|
@@ -148,8 +155,157 @@ class ScriptrouteTest < Test::Unit::TestCase
|
|
148
155
|
def test_nameify
|
149
156
|
puts Scriptroute.nameify("12.123.203.170")
|
150
157
|
end
|
158
|
+
|
159
|
+
# pretty much checks whether the packet gets filled in.
|
160
|
+
# manually constructed with pack, rather than via
|
161
|
+
# scriptroute/packets
|
162
|
+
def test_v6
|
163
|
+
daddr = IPAddr.new "ff02::fb" # "fd00::230:67ff:febb:7d18"
|
164
|
+
# daddr = IPAddr.new "fe80::60c:ceff:fee4:a62" # wlp
|
165
|
+
daddr = IPAddr.new "fd00::60c:ceff:fee4:a62" # wlp
|
166
|
+
saddr = IPAddr.new "::0"
|
167
|
+
# saddr = IPAddr.new "fd00::230:67ff:febb:7d18" # zar
|
168
|
+
# saddr = IPAddr.new "fe80::230:67ff:febb:7d18" # fd00::230:67ff:febb:7d18"
|
169
|
+
payload_len = 40 #
|
170
|
+
|
171
|
+
# hard coded the cksum.
|
172
|
+
icmp = [ Scriptroute::ICMP6::ICMP6_ECHO, 0, 0xc12b, 0, 1 ].pack("ccnnn") + ( "\0" * 56 )
|
173
|
+
v6packet = [ 6 << 4, 0, 0, icmp.length, Scriptroute::IPv6::IPPROTO_ICMP6,
|
174
|
+
64, saddr.hton, daddr.hton ].pack("ccnncca16a16") + icmp
|
175
|
+
|
176
|
+
raise "too short (#{v6packet.length})" unless v6packet.length >= 40
|
177
|
+
puts v6packet.length
|
178
|
+
puts v6packet.unpack("H*")
|
179
|
+
|
180
|
+
p = Scriptroute::send_train([ Struct::DelayedPacket.new(0, v6packet) ])
|
181
|
+
puts p
|
182
|
+
|
183
|
+
p[0].probe or raise "couldn't manage sending a v6 probe"
|
184
|
+
p[0].probe.packet or raise "couldn't manage sending a v6 probe packet"
|
185
|
+
p[0].probe.packet.ip6_src or raise "v6 probe packet lacks a source"
|
186
|
+
p[0].probe.packet.ip6_src != IPAddr.new("::0") or raise "v6 probe packet source seems to be unset."
|
187
|
+
p[0].probe.packet.icmp_cksum != 0 or raise "v6 probe packet 99.99% likely unset checksum."
|
188
|
+
end
|
189
|
+
|
190
|
+
# tries to build an medium level of easiness ipv6 ping.
|
191
|
+
def test_v6_lookup
|
192
|
+
daddr = nil
|
193
|
+
Resolv::DNS.open do |dns|
|
194
|
+
ress = dns.getresources "www.google.com", Resolv::DNS::Resource::IN::AAAA
|
195
|
+
ress.each { |r| daddr = r.address.to_s }
|
196
|
+
end
|
197
|
+
raise "no address" unless daddr
|
198
|
+
p = Scriptroute::ICMP6echo.new(20)
|
199
|
+
p.ip6_dst = daddr
|
200
|
+
begin
|
201
|
+
r = Scriptroute::send_train([ Struct::DelayedPacket.new(0, p) ])
|
202
|
+
puts r
|
203
|
+
rescue Scriptroute::ScriptrouteError => e
|
204
|
+
if e.to_s =~ /unreachable/ then
|
205
|
+
puts "Expected ERROR when host supports v6 but lacks routes to the outside world."
|
206
|
+
else
|
207
|
+
puts "rescued unexpected error: " + e.to_s
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
def test_v6_localhost
|
212
|
+
p = Scriptroute::ICMP6echo.new(20)
|
213
|
+
p.ip6_dst = IPAddr.new("::1")
|
214
|
+
p.ip6_dst = "::1"
|
215
|
+
r = Scriptroute::send_train([ Struct::DelayedPacket.new(0, p) ])
|
216
|
+
rescue Scriptroute::ScriptrouteError => e
|
217
|
+
puts "Expected ERROR because localhost is filtered."
|
218
|
+
end
|
219
|
+
def test_v6_implicit_lookup
|
220
|
+
p = Scriptroute::UDP6.new(20)
|
221
|
+
p.ip6_dst = "www.google.com"
|
222
|
+
puts p.ip6_dst
|
223
|
+
end
|
224
|
+
|
225
|
+
# I don't know whether I want to be able to send to all-routesr
|
226
|
+
def test_v6_mcast
|
227
|
+
failure = false
|
228
|
+
begin
|
229
|
+
p = Scriptroute::ICMP6echo.new(20)
|
230
|
+
p.ip6_dst = IPAddr.new("ff02::1") # all nodes.
|
231
|
+
r = Scriptroute::send_train([ Struct::DelayedPacket.new(0, p) ])
|
232
|
+
failure = true if r[0].probe
|
233
|
+
rescue Scriptroute::ScriptrouteError => e
|
234
|
+
puts "Expected ERROR because multicast is filtered or source cannot be set."
|
235
|
+
end
|
236
|
+
begin
|
237
|
+
p = Scriptroute::ICMP6echo.new(20)
|
238
|
+
p.ip6_dst = IPAddr.new("ff02::2") # all routers.
|
239
|
+
r = Scriptroute::send_train([ Struct::DelayedPacket.new(0, p) ])
|
240
|
+
failure = true if r[0].probe
|
241
|
+
rescue Scriptroute::ScriptrouteError => e
|
242
|
+
puts "Expected ERROR because multicast is filtered or source cannot be set."
|
243
|
+
end
|
244
|
+
failure
|
245
|
+
end
|
246
|
+
|
247
|
+
def test_v6_onehop
|
248
|
+
daddr = nil
|
249
|
+
Resolv::DNS.open do |dns|
|
250
|
+
ress = dns.getresources "www.google.com", Resolv::DNS::Resource::IN::AAAA
|
251
|
+
ress.each { |r| daddr = r.address.to_s }
|
252
|
+
end
|
253
|
+
raise "no address" unless daddr
|
254
|
+
p = Scriptroute::ICMP6echo.new(20)
|
255
|
+
p.ip6_dst = daddr
|
256
|
+
p.ip6_hlim = 1
|
257
|
+
begin
|
258
|
+
r = Scriptroute::send_train([ Struct::DelayedPacket.new(0, p) ])
|
259
|
+
if r[0].response then
|
260
|
+
puts "success from first hop: %s:" % r[0].response.packet.ip6_src
|
261
|
+
puts r[0].response.packet
|
262
|
+
true
|
263
|
+
else
|
264
|
+
puts "failed: expected to see a response to the one-hop ttl limited probe"
|
265
|
+
false
|
266
|
+
end
|
267
|
+
rescue Scriptroute::ScriptrouteError => e
|
268
|
+
if e.to_s =~ /unreachable/ then
|
269
|
+
puts "Expected ERROR when host supports v6 but lacks routes to the outside world."
|
270
|
+
else
|
271
|
+
puts "rescued unexpected error: " + e.to_s
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def test_v6_udp_tr
|
277
|
+
daddr = nil
|
278
|
+
Resolv::DNS.open do |dns|
|
279
|
+
ress = dns.getresources "www.google.com", Resolv::DNS::Resource::IN::AAAA
|
280
|
+
ress.each { |r| daddr = r.address.to_s }
|
281
|
+
end
|
282
|
+
raise "no address" unless daddr
|
283
|
+
p = Scriptroute::UDP6.new(20)
|
284
|
+
p.ip6_dst = daddr
|
285
|
+
puts p.to_s
|
286
|
+
begin
|
287
|
+
[4,5,6,20].each { |ttl|
|
288
|
+
p.ip6_hlim = ttl
|
289
|
+
r = Scriptroute::send_train([ Struct::DelayedPacket.new(0, p) ])
|
290
|
+
if r[0].response then
|
291
|
+
puts "success from hop %d: %s:" % [ ttl, r[0].response.packet.ip6_src ]
|
292
|
+
puts r[0].response.packet
|
293
|
+
true
|
294
|
+
else
|
295
|
+
puts "failed: expected to see a response to the one-hop ttl limited probe"
|
296
|
+
false
|
297
|
+
end
|
298
|
+
}
|
299
|
+
rescue Scriptroute::ScriptrouteError => e
|
300
|
+
if e.to_s =~ /unreachable/ then
|
301
|
+
puts "Expected ERROR when host supports v6 but lacks routes to the outside world."
|
302
|
+
else
|
303
|
+
puts "rescued unexpected error: " + e.to_s
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
151
307
|
end
|
152
308
|
|
153
309
|
# Local Variables:
|
154
|
-
# compile-command: "
|
310
|
+
# compile-command: "ruby -I ../lib ./test_scriptroute.rb"
|
155
311
|
# End:
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scriptroute
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Neil Spring
|
@@ -27,11 +27,13 @@ files:
|
|
27
27
|
- bin/sr-ping-T
|
28
28
|
- bin/sr-rockettrace
|
29
29
|
- bin/sr-traceroute
|
30
|
+
- bin/sr-traceroute6
|
30
31
|
- bin/tulip
|
31
32
|
- lib/scriptroute.rb
|
32
33
|
- lib/scriptroute/ally.rb
|
33
34
|
- lib/scriptroute/commando.rb
|
34
35
|
- lib/scriptroute/fixclock
|
36
|
+
- lib/scriptroute/ipaddr4.rb
|
35
37
|
- lib/scriptroute/liveness.rb
|
36
38
|
- lib/scriptroute/nameify.rb
|
37
39
|
- lib/scriptroute/packets.rb
|
@@ -41,6 +43,7 @@ files:
|
|
41
43
|
- lib/scriptroute/tulip/queuing.rb
|
42
44
|
- lib/scriptroute/tulip/reordering.rb
|
43
45
|
- test/test_bins.rb
|
46
|
+
- test/test_commando.rb
|
44
47
|
- test/test_scriptroute.rb
|
45
48
|
homepage: http://www.scriptroute.org/
|
46
49
|
licenses:
|