Skip to main content

Julia Evans

Day 20: Traceroute in 15 lines of code using Scapy

Today Jari and Brian explained a whole bunch of things to me about networks! It was fantastic. It’s amazing to have people with so much experience to ask questions.

At the end they mentioned that I should look up how traceroute works and that it’s a pretty popular networking-job-interview question. And I’d just discovered this super cool Python networking library called Scapy which lets you construct packets really easily. So I thought I’d implement traceroute using scapy!

I thought it would take a long time, but turns out that (a basic version) is really easy.

So using scapy, you can create IP and UDP packets like this:

from scapy.all import *
ip_packet = IP(dst="hackerschool.com", ttl=10)
udp_packet = UDP(dport=40000)
full_packet = IP(dst="hackerschool.com", ttl=10) / UDP(dport=40000)

Then you can send a packet like this:

send(full_packet)

So IP packets have a ttl attribute, which stands for “Time-To-Live”. Every time a machine receives an IP packet, it decreases the ttl by 1 and passes it on. Basically this is a super smart way to make sure that packets don’t get into infinite loops.

If a packet’s ttl runs out before it replies, the last machine sends back an ICMP packet saying “sorry, failed!”.

To implement traceroute, we send out a UDP packet with ttl=i for i = 1,2,3,.... Then we look at the reply packet and see if it’s a “Time ran out” or “That port doesn’t exist” error message. In the first case, we keep going, and in the second case we’re done.

Here’s the code! It’s 16 lines including comments and everything.

from scapy.all import *
hostname = "google.com"
for i in range(1, 28):
    pkt = IP(dst=hostname, ttl=i) / UDP(dport=33434)
    # Send the packet and get a reply
    reply = sr1(pkt, verbose=0)
    if reply is None:
        # No reply =(
        break
    elif reply.type == 3:
        # We've reached our destination
        print "Done!", reply.src
        break
    else:
        # We're in the middle somewhere
        print "%d hops away: " % i , reply.src

The output looks like:


1 hops away:  192.168.1.1
2 hops away:  24.103.20.129
3 hops away:  184.152.112.73
4 hops away:  184.152.112.73
5 hops away:  107.14.19.22
...

So it turns out traceroute is kind of easy! Apparently the difference between traceroute on Windows and Unix is that Unix generally sends UDP packets and Windows sends ICMP packets.

There’s also tcptraceroute which, well, sends TCP packets.

Day 18: ARP cache poisoning (or: In ur connection, sniffing ur packets) Day 19: A few reasons why networking is hard