Evan Pratten
Evan Pratten
Software Developer

NOTICE: The service mentioned in this post is currently unavailable due to ongoing network upgrades.

mtr (a modern version of traceroute) is a network debugging tool commonly used by network engineers to trace the physical (and sometimes virtual) paths their packets take between two computers over a network. Both mtr and traceroute will list the addresses or names of as many routers along the path as they can.

The following is an example output of an mtr trace from this computer to a Hurricane Electric server:

MTR command output

Traceroute toys

Over time, a few exceptionally bored network engineers have created some fun services that piggyback off of this idea of listing hosts along a path.

For example, there is:

That last one was the inspiration for this project, and I will likely reference Ben's post a fair bit in this one. If you are interested in the lower level technical aspects of what I have set up, go read his post, as I am running a nearly identical setup to him.

The game plan

My intent for this project was rather simple: rickroll people when they traceroute my website. Now, I technically failed at most of that, and the result is really: rickroll people when they mtr part of my website. This change of scope was simply due to the fact I'd rather not introduce unwanted latency into the regular viewing experience of this site.

The steps were as follows:

  1. Allocate an IPv6 address block for the project
  2. Set up Reverse DNS
    1. Convert the lyrics of Never Gonna Give You Up to FQDN format
    2. Register the appropriate PTR records
  3. Dark magic
  4. Profit!

IP addresses and RDNS

For this project, I ended up using the public address block 2a06:a005:d2b:c011::/64, as I already own and control the routing for its parent block.

I then delegated reverse DNS to the Hurricane Electric DNS service for easy management. I'll get back to this in a second.

Converting lyrics to FQDNs

For this process, I ended up building a little Python tool called lyrics2ptr that generates copy-pastable Hurricane Electric settings from a text file full of lyrics.

The specific command for this was:

python3 lyrics2ptr.py /path/to/rickroll-lyrics.txt --addr-prefix c011::

Thus generating output like this:

c011::0008 never.gonna.give.you.up
c011::0009 never.gonna.let.you.down
c011::000a never.gonna.run.around.and.desert.you
c011::000b never.gonna.make.you.cry
c011::000c never.gonna.say.goodbye
c011::000d never.gonna.tell.a.lie.and.hurt.you

While the output format may look weird, it directly corresponds to the input fields in the DNS control panel.

A screenshot of the HE control panel

ICMPv6 trickery

Now, for the fun complicated part of this project.

When you run an mtr or traceroute against a remote host, your computer will send out arbitrary packets to that host, but slowly increment the Time to live (TTL) field in such packets.

As a refresher, the TTL field is a number that is decremented every time a packet is passed through a router. If this number ever hits zero, the packet is discarded and an ICMPv6 Time Exceeded packet is returned to the sender indicating that their packet spent too long in transit. This mechanism exists to help prevent routing loops.

Generally, this field is set to a value of 64, but tracing programs will make it low numbers to attempt to get otherwise hidden routers to announce their presence via a Time Exceeded packet.

For a router to show up with a name in the mtr output, it must both send a Time Exceeded packet back to your host, and have a reverse DNS record registered (thus, why I did that in the previous section).

Aren't you going to need a ton of hosts?

Generally, such a setup would involve daisy-chaining routers physically in your network, and setting each of their hostnames, so clients would physically have their packets routed between each of the routers, and get an ICMP response from each of them. This is a lot of work.

Conveniently for my wallet, Linux machines provide something called Tun/Tap Interfaces. These virtual network interfaces allow programs to pretend to be one of many other computers on the network and act as if they were real hosts. When a program registers one of these interfaces with the kernel, it gets raw access to either the 2nd or 3rd OSI layer of the network stack in the form of a raw stream.

Tun/Tap in the OSI stack

I chose to register a Tun interface, and control things at the Internet Protocol level. This choice was mainly due to simplicity, as I really don't care about hardware addresses.

Lying about routers

At this point, someone can perform mtr on my Tun interface address, and they will get... nothing.

But, importantly, my program sees their request (again, as raw bytes) and has complete control over the response (I could just send DEADBEEFDEADBEEFDEADBEEF if I wanted in place of a real IP packet).

When an ICMPv6 Echo Request packet (the type that mtr sends for queries) comes in, I simply grab two things: the source address and the ttl field.

Quick sidenote: the way I had configured my RDNS records, each line of lyrics works out to the next host in line.

The PTR record list

With my RDNS setup, and the fact mtr will increment the TTL field for every router it wants to find, the process for mapping a "next router please" request to a "here is the next line of lyrics instead" response is simply to use the TTL itself as the host part of the IP address I pretend to respond from.

Thus, when mtr looks for the third router in line, it'll get the address 2a06:a005:d2b:c011::3 in response, and resolve it to you.know.the.rules.and.so.do.i.


Well, thats about it. I skipped over some implementation details, but if you'd like to check out the source code for this project, head over to its GitHub page:

And for the end result:

The result

Try it yourself!

mtr -rwc 1 rickroll.as204466.va3zza.com