Python Traceroute Style Tool

Recently, while talking with a some techies I was asked to explain how traceroute works. I was quick to answer what results I expect back from the command, and how to understand that data, but for the life of me I was having issues recalling how TTL works at the foundation.

Later that week I spent some time reading up on it, and click, it all came back.

To reinforce this I decided to write some code. I decided to use Scapy in order to make crafting network packets a breeze (shown below). You should be able to get Scapy right from PyPi:

$ sudo pip install scapy

tracehop.py

#!/usr/bin/env python

# avoid printing some ipv6 debug stuff from scapy.
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)

from scapy.all import IP, ICMP, sr1

from sys import argv

def tracehop(host_or_ip):
  """
  Return the network hops used to arrive at destination.
  """
  # craft our IP portion of the packet with destination address.
  ip = IP(dst=host_or_ip)

  # use a blank ICMP packet.
  icmp = ICMP()

  # set our time to live to 1, this will get our first hop.
  ip.ttl = 1

  # we will loop until getting a echo-response.
  while True:

    # send the ICMP packet and expect one response.
    res = sr1(ip/icmp, verbose=False)

    # print out the responding host source address.
    print res['IP'].src

    # if our ICMP type is 0 we've got
    # the echo response and can break while loop.
    # http://www.nthelp.com/icmp.html
    if res['ICMP'].type == 0:
      break

    # if we did not break above we increase the ttl by
    # one, and let the while loop run once again.
    ip.ttl += 1

if __name__ == '__main__':
  tracehop(argv[1])

The above code should be documented well enough to answer most questions.

Python is unable to create network sockets without root privileges. I do not advise running any script you find on the internet as root until reading over it, and fully understanding what may happen to your system and why root privileges are needed.

With that out of the way lets get right to the usage and output:

$ sudo python tracehop.py nessy.info
192.168.0.1
10.82.16.1
68.4.12.108
68.4.11.68
68.1.1.167
129.250.194.165
129.250.4.42
129.250.3.237
129.250.4.150
129.250.3.27
129.250.5.32
129.250.204.118
198.199.99.238
104.236.149.49

We can compare traceroute’s results to ours, and make sure the hops are relatively similar:

$ traceroute -n nessy.info
traceroute to nessy.info (104.236.149.49), 30 hops max, 60 byte packets
 1 192.168.0.1 19.107 ms 19.470 ms 19.315 ms
 2 10.82.16.1 19.208 ms 28.023 ms 28.233 ms
 3 68.4.12.108 28.142 ms 28.034 ms 27.901 ms
 4 68.4.11.68 27.799 ms 27.683 ms 27.557 ms
 5 68.1.1.167 27.426 ms 27.330 ms *
 6 129.250.194.165 27.095 ms 11.672 ms 18.996 ms
 7 129.250.4.42 27.821 ms 27.727 ms 32.801 ms
 8 129.250.3.237 15.742 ms 20.731 ms 17.218 ms
 9 129.250.4.150 28.956 ms 28.876 ms 28.787 ms
10 129.250.3.27 31.914 ms 32.112 ms 28.468 ms
11 129.250.4.118 28.356 ms 129.250.5.32 31.783 ms 25.326 ms
12 129.250.203.82 23.522 ms 30.280 ms 129.250.204.118 26.967 ms
13 198.199.99.238 26.921 ms 198.199.99.234 31.626 ms 198.199.99.254 27.053 ms
14 104.236.149.49 26.340 ms 26.614 ms 27.644 ms

Then again we could just proof this using traceroute , the -m flag sets our TTL:

$ traceroute -m 1 8.8.8.8
traceroute to 8.8.8.8 (8.8.8.8), 1 hops max, 60 byte packets
 1 192.168.0.1 (192.168.0.1) 3.398 ms 3.592 ms 3.482 ms

And then hack together quickly a bash script:

$ for i in $(seq 1 30) ; do
     traceroute -m $i -n nessy.info | tail -n 1 | egrep "$i " | awk '{print $2}'
  done

192.168.0.1
10.82.16.1
68.4.12.108
68.4.11.68
68.1.1.167
129.250.194.165
129.250.4.42
129.250.3.237
129.250.4.150
129.250.3.121
129.250.4.118
129.250.204.118
198.199.99.254
104.236.149.49

And now the inner-workings of traceroute should be stuck in my head, never to be tripped up again, or at least that is the plan ;)