Update: Belorussian translation.
At Edgeio we had a fairly complicated network setup, and at one point I quickly hacked together a Ruby script to merge the paths generated by multiple traceroute runs together into directed graphs, showing the routing from a few selected host in our environment to all our other hosts. I generated dot-files suitable for Graphviz from it.
It was a helpful way of looking for weird inconsistencies in routing, in particular between our two locations.
Unfortunately, when Edgeio closed down I think the script was lost, and in any case if it isn't I wouldn't be able to get permission to release it without more hassle than it'd take me to recreate it from scratch. So recreate it from scratch is exactly what I did.
Here's an example of a traceroute from www.hokstad.com to www.gmail.com, docs.google.com and www.google.com (scaled down):
(Gradients and shadows courtesy of my XSL transform to make graphviz output prettier)
There's a couple of caveats: I just strip out failed probes, and I don't try to reconcile the names of the endpoints (which I preferred to include for readability) with the IP addresses of the trace, so the first/last grey nodes before the named/blue nodes may be redundant.
This script by default runs traceroute 3 times for each target, and that's the reason why there are more possible paths than endpoints, and it illustrates failover and/or load balancing mostly, but can also be affected by fluctuations in dynamic routing. It's usually fairly stable, and in fact at Edgeio I found several network problems by re-running the script when something was up and looking at how the routing had changed. 3 runs seemed sufficient for my use, but for large networks adding more may give a better picture of the routing.
You can find the full script here, but here are the guts:
First I defined a convenience method to run traceroute and capture the output. This is intended for a POSIX OS (Linux / Unix / BSD's), but mainly requires a working traceroute where the output is a number of lines starting with a hop count and then the ip address. TRACEROUTE must be set to a valid traceroute command.
TRACEROUTE=`which traceroute`.chomp
def traceroute host
`#{TRACEROUTE} -n #{host}`
end
The TraceViz class does the gruntwork. @edges contains the edges of the graph, in other words which pairs of ip addresses represent a hop further in the network. @nodes contains a set of the ip addresses found. @targets contains the hostnames of the start and end-points - it's used only to style them differently:
class TraceViz
def initialize(times,timeout)
@times,@timeout = times,timeout
@edges = Set.new
@nodes = Set.new
@targets = Set.new
@this_host = Socket.gethostname
@targets << @this_host
end
</pre>
The #trace method executes the traceroutes, and enforces a timeout:
def trace host
@times.times do |i|
STDERR.puts "Trace ##{i+1} for #{host}"
Timeout::timeout(@timeout) do
process_trace(host,traceroute(host))
end rescue nil
end
end
#process_trace handles the parsing of the trace, by splitting the output into lines, extracting the IP addresses, and then adding each of them to @nodes, and adding each pair to @edges. I don't care if we've seen them before, since I use Set's so the previous (identical) nodes/edges will just overwrite the same values:
def process_trace host,trace
@targets << host
trace = [@this_host] + trace.collect do |line|
line=line.split
line[0].to_i > 0 && line[1] != "*" ? line[1] : nil
end.compact
trace << host
trace.each {|h| @nodes << h }
trace.each_cons(2) {|h1,h2| @edges << [h1,h2] }
end
Finally #to_dot generates a graphviz compatible directed graph:
def to_dot
res = "digraph G {"
@edges.each { |h1,h2| res << " \"#{h1}\"->\"#{h2}\"\n" }
@nodes.each do |n|
color = @targets.member?(n) ? "lightblue" : "lightgrey"
res << " \"#{n}\" [style=filled fillcolor=#{color}]\n"
end
res << "}"
end
end
First run the script to generate the dot-files, and then generate an SVG file from it:
ruby traceviz.rb www.google.com www.gmail.com docs.google.com >trace.dot dot -Tsvg trace.dot >trace.svg
Optionally, process the script with my XSL transform to make it prettier (adding the gradients from above etc) - I'm using xsltproc from libxslt:
xsltproc notugly.xsl trace.svg >trace-notugly.svg
Then I used "rsvg" from librsvg2 to turn it into a PNG:
rsvg trace-notugly.svg traceviz.png
Of course these steps are easily enough wrapped into a script.