flyingroutes: a faster alternative to traceroute
Overview
In this article, I introduce a network diagnostic tool I developed: flyingroutes. The goal is to provide an efficient way to determine the nodes located on a network communication path through a faster alternative to the well-known traceroute tool.
Introduction
Diagnostics is a crucial part of a network administrator’s job. To achieve this, many tools exist to check the presence of a host on the network, discover the services a host can offer, find the paths to reach a host, or determine the amount of data that can be transmitted per data packet sent over a network. Here, we focus on the network path discovery function from a source host to a destination host. We will first review the mode of operation of the well-known traceroute tool before introducing the alternative I developed: flyingroutes.
Origins of traceroute
traceroute is, along with ping, one of the most well-known and widely used network utilities by network administrators. The original version dates back to 1987 and was first developed at the Lawrence Berkeley National Laboratory (LBNL) at the University of California. traceroute enables the sending of packets over an Internet Protocol (IP) network sequentially to a given target by increasing the value of the Time To Live (TTL) IP field, starting at 1 and going up to n, with n being the number of nodes or being crossed until, and including, the target. Each time a packet reaches a node, the node decrements the TTL value by 1, and if the value reaches 0, the received packet is not forwarded to the next node, and a specific Internet Control Message Protocol (ICMP) TTL exceeded response is sent back to the sender. Thus, the first packet is dropped by the first node, the second packet is dropped by the second node, and so on until the value of n is large enough to eventually reach the target, which will provide an appropriate response to the packet. The IP packets sent as probing message can contain data based on the ICMP, User Datagram Protocol (UDP), or Transmission Control Protocol (TCP) protocols. This process is illustrated ion the figure below.

In most cases, an ICMP TTL exceeded message is sent back by the nodes along the path, but sometimes they do not respond to this mechanism and thus do not send a message. This results in the presence of the infamous small asterisks (*) in the command output. And that’s precisley when the network administrator might have time to grab a coffee if the asterisks keep multiplying! Even when setting a timeout (with the -w
option), I had to wait nearly 3 minutes before receiving the complete output of the command below.
$ time traceroute thibautprobst.fr
traceroute: Warning: thibautprobst.fr has multiple addresses; using 99.86.91.20
traceroute to thibautprobst.fr (99.86.91.20), 64 hops max, 52 byte packets
1 lan.home (192.168.1.254) 6.598 ms 2.960 ms 2.991 ms
2 80.10.237.205 (80.10.237.205) 7.750 ms 4.375 ms 4.004 ms
3 lag-10.neblc00z.rbci.orange.net (193.253.84.82) 14.324 ms 5.207 ms 5.010 ms
4 ae87-0.nctou201.rbci.orange.net (193.253.83.242) 13.275 ms 12.602 ms 5.830 ms
5 ae43-0.nipoi201.rbci.orange.net (193.252.160.49) 14.314 ms 12.417 ms 14.346 ms
6 193.252.137.18 (193.252.137.18) 18.060 ms 16.425 ms 17.128 ms
7 99.83.114.168 (99.83.114.168) 26.709 ms
193.251.248.38 (193.251.248.38) 18.959 ms
193.251.248.36 (193.251.248.36) 19.835 ms
8 * * *
9 * * *
10 * * *
11 * * *
12 * * *
13 * * *
14 * * *
15 * * *
16 * * *
17 * * *
18 server-99-86-91-20.cdg50.r.cloudfront.net (99.86.91.20) 20.457 ms * *
real 2m40.437s
user 0m0.004s
sys 0m0.018s
So, what do you do if you don’t like coffee or if you’re not very patient?
A faster alternative: flyingroutes
There I propose an alternative with flyingroutes. You no longer need to wait for the complete output of your traceroute command! Indeed, you get a similar result in… nearly 3 seconds!
$ time python3 flyingroutes thibautprobst.fr
flyingroutes to thibautprobst.fr (99.86.91.101) with 30 hops max (3 packets per hop) on ICMP with a timeout of 2.0s
thibautprobst.fr (99.86.91.101) reached in 20 hops
Hop 1: 192.168.1.254 (lan.home) - 25.36ms
Hop 2: 80.10.237.205 - 24.81ms
Hop 3: 193.253.84.82 (lag-10.neblc00z.rbci.orange.net) - 25.5ms
Hop 4: 193.253.83.242 (ae87-0.nctou201.rbci.orange.net) - 24.57ms
Hop 5: 193.252.160.49 (ae43-0.nipoi201.rbci.orange.net) - 25.82ms
Hop 6: 193.252.137.18 - 29.2ms
Hop 7: 193.251.248.148 - 32.28ms
Hop 8: * * * * * * * *
Hop 9: * * * * * * * *
Hop 10: * * * * * * * *
Hop 11: * * * * * * * *
Hop 12: * * * * * * * *
Hop 13: * * * * * * * *
Hop 14: * * * * * * * *
Hop 15: * * * * * * * *
Hop 16: * * * * * * * *
Hop 17: * * * * * * * *
Hop 18: * * * * * * * *
Hop 19: * * * * * * * *
Hop 20: 99.86.91.101 (server-99-86-91-101.cdg50.r.cloudfront.net) - 20.78ms
real 0m3.271s
user 0m0.168s
sys 0m0.049s
To address this, I simply avoid the sequential method of traceroute. In fact, I send a set of packets simultaneously with different TTL values (between 1 and n, where n is 30 by default, but you can adjust this), and I wait for all the node responses, which are then matched to the initial requests. This is made possible through multithreading. The received responses can be:
- ICMP TTL exceeded when the TTL expires on a node;
- ICMP Echo Reply in response to an ICMP Echo Request;
- ICMP Destination unreachable (Port unreachable) in response to a UDP request;
- TCP SYN/ACK if a TCP SYN request is answered by a service on the target;
- TCP RST if a TCP SYN request is not answered by any service on the target.

The challenge of flyingroutes lies in re-associating the responses with the initial requests. Indeed, parallelism introduces the possibility of receiving responses out of order, thus requiring indicators for requests and responses associations. Here is how I tackled the challenge in each case:
- In ICMP:
- Association of ICMP Echo request with ICMP TTL exceeded responses: I store the calculated ICMP checksum sent in the ICMP Echo Request packets, and then I read the value of this checksum stored in the payload of the received ICMP TTL exceeded responses.
- Association of ICMP Echo request with ICMP Echo Reply responses: I store the IP TTL value in the payload of the ICMP Echo Request packets as this payload is returned in the ICMP Echo Reply responses, which I then read.
- In UDP:
- Association of UDP requests with ICMP TTL exceeded responses: I store the source UDP port used to send the UDP requests, then I read the value of this port which is stored in the payload of the received ICMP TTL exceeded responses.
- Association of UDP requests with ICMP Destination unreachable (Port unreachable) responses: I store the source UDP port used to send the UDP requests, then I read the value of this port which is stored in the payload of the ICMP Destination unreachable (Port unreachable) responses.
- In TCP:
- Association of TCP SYN requests with ICMP TTL exceeded responses: I attempt to establish TCP sessions by sending TCP SYN packets, and for each session, I check its state (established or not) by sending data and verifying the result. If the session is open, the data can be sent, so I store the source TCP port used to send the TCP messages, then I read the value of this port stored in the payload of the received ICMP TTL exceeded responses.
- Association of TCP SYN requests with TCP SYN/ACK responses: I attempt to establish TCP sessions by sending TCP SYN packets, and for each session, I check its state (established or not) by sending data and verifying the result. If the session is not open, the data cannot be sent, meaning no TCP SYN/ACK response was received.
In addition to being able to set the maximum number of nodes to test, flyingroutes supports, as you may have guessed, ICMP, UDP, and TCP. You can even use all three protocols simultaneously, which maximizes the chances of discovering nodes—, a feature that, to my knowledge, is not offered by any similar tool. You can also choose the port used for UDP and TCP (33434 by default), specify a timeout value for those who are really impatient (with the risk of reducing the chances of node discovery, with the default value being 2s), and set the number of packets sent per TTL value (3 by default, allowing you to check if different paths exist through a node by varying the port). Additionally, note that flyingroutes is developed in Python (version 3.10 minimum required) and is supported on MacOS, Linux, and Windows.
$ python3 flyingroutes.py -h
usage: flyingroutes.py [-h] [--number_of_hops NUMBER_OF_HOPS] [--protocol PROTOCOL] [--dest_port DEST_PORT] [--timeout TIMEOUT] [--repeat REPEAT] HOST
positional arguments:
HOST target host
options:
-h, --help show this help message and exit
--number_of_hops NUMBER_OF_HOPS, -n NUMBER_OF_HOPS
Max number of hops allowed to reach the target (default: 30)
--protocol PROTOCOL, -p PROTOCOL
Protocol to use: ICMP, UDP, TCP or ALL of them (default: ICMP)
--dest_port DEST_PORT, -d DEST_PORT
Port to use for UDP and TCP only (default: 33434), increased by 1 for each additional packets sent with the --repeat option
--timeout TIMEOUT, -t TIMEOUT
Timeout for responses (default: 2s)
--repeat REPEAT, -r REPEAT
Number of packets to repeat per TTL value increase using different destination ports (default: 3, max: 16)
Keep an eye on the GitHub repository because more features are coming! Feel free to request new features or report bugs Feel free to request new features or report bugs here. flyingroutes is a pretty young project, so I need constructive feedback!
Here are a few examples of using flyingroutes.
$ python3 flyingroutes.py example.com -p tcp -d 443 -r 5 -n 20
flyingroutes to example.com (93.184.216.34) with 20 hops max (5 packets per hop) on TCP port 443 with a timeout of 2.0s
example.com (93.184.216.34) reached in 13 hops
Hop 1: 192.168.1.254 (lan.home) - 40.29ms
Hop 2: * * * * * * * *
Hop 3: * * * * * * * *
Hop 4: 193.253.83.242 (ae87-0.nctou201.rbci.orange.net) - 159.63ms
Hop 5: 193.252.160.49 (ae43-0.nipoi201.rbci.orange.net) - 267.23ms
Hop 6: 193.252.137.18 - 491.86ms
Hop 7: 129.250.66.144 (ae-26.a01.parsfr05.fr.bb.gin.ntt.net) - 365.06ms
Hop 8: 129.250.2.178 (ae-15.r20.parsfr04.fr.bb.gin.ntt.net) - 687.11ms
129.250.2.106 (ae-15.r21.parsfr04.fr.bb.gin.ntt.net) - 590.88ms
Hop 9: 129.250.6.6 (ae-13.r24.asbnva02.us.bb.gin.ntt.net) - 866.76ms
129.250.4.194 (ae-14.r21.nwrknj03.us.bb.gin.ntt.net) - 726.34ms
Hop 10: 129.250.3.17 (ae-1.a02.nycmny17.us.bb.gin.ntt.net) - 1238.61ms
129.250.2.145 (ae-0.a04.asbnva02.us.bb.gin.ntt.net) - 1079.23ms
129.250.3.242 (ae-0.a05.asbnva02.us.bb.gin.ntt.net) - 942.95ms
Hop 11: 129.250.192.98 (ce-1-1-3.a05.asbnva02.us.ce.gin.ntt.net) - 1118.77ms
128.241.1.90 (ce-0-13-0-3.r01.nycmny17.us.ce.gin.ntt.net) - 825.45ms
128.241.1.14 (ce-0-3-0.a02.nycmny17.us.ce.gin.ntt.net) - 785.09ms
Hop 12: 152.195.65.129 (ae-66.core1.dcb.edgecastcdn.net) - 1351.22ms
152.195.64.129 (ae-65.core1.dcb.edgecastcdn.net) - 1292.69ms
152.195.68.131 (ae-65.core1.nyb.edgecastcdn.net) - 1039.99ms
152.195.69.131 (ae-66.core1.nyb.edgecastcdn.net) - 1000.56ms
Hop 13: 93.184.216.34 - 101.79ms
$ python3 flyingroutes.py 8.8.8.8 -p udp -r 1
flyingroutes to 8.8.8.8 (8.8.8.8) with 30 hops max (1 packets per hop) on UDP port 33434 with a timeout of 2.0s
8.8.8.8 (8.8.8.8) reached in 11 hops
Hop 1: 192.168.1.254 (lan.home) - 23.69ms
Hop 2: 80.10.237.205 - 34.01ms
Hop 3: 193.253.84.82 (lag-10.neblc00z.rbci.orange.net) - 30.41ms
Hop 4: 193.253.83.242 (ae87-0.nctou201.rbci.orange.net) - 49.86ms
Hop 5: 193.252.160.49 (ae43-0.nipoi201.rbci.orange.net) - 49.87ms
Hop 6: 193.252.160.46 (ae40-0.nipoi202.rbci.orange.net) - 59.34ms
Hop 7: 193.252.137.14 - 78.51ms
Hop 8: 74.125.50.250 - 98.09ms
Hop 9: 108.170.244.161 - 129.06ms
Hop 10: 142.251.49.135 - 108.13ms
Hop 11: 8.8.8.8 (dns.google) - 27.62ms
$ python3 flyingroutes.py thibautprobst.fr -t 3
flyingroutes to thibautprobst.fr (99.86.91.20) with 30 hops max (3 packets per hop) on ICMP with a timeout of 3.0s
thibautprobst.fr (99.86.91.20) reached in 18 hops
Hop 1: 192.168.1.254 (lan.home) - 65.18ms
Hop 2: 80.10.237.205 - 60.73ms
Hop 3: 193.253.84.82 (lag-10.neblc00z.rbci.orange.net) - 56.52ms
Hop 4: 193.253.83.242 (ae87-0.nctou201.rbci.orange.net) - 65.61ms
Hop 5: 193.252.160.49 (ae43-0.nipoi201.rbci.orange.net) - 57.91ms
Hop 6: 193.252.137.18 - 88.39ms
Hop 7: 193.251.248.38 - 75.2ms
Hop 8: * * * * * * * *
Hop 9: * * * * * * * *
Hop 10: * * * * * * * *
Hop 11: * * * * * * * *
Hop 12: * * * * * * * *
Hop 13: * * * * * * * *
Hop 14: * * * * * * * *
Hop 15: * * * * * * * *
Hop 16: * * * * * * * *
Hop 17: * * * * * * * *
Hop 18: 99.86.91.20 (server-99-86-91-20.cdg50.r.cloudfront.net) - 51.61ms
$ python3 flyingroutes.py thibautprobst.fr -p all
flyingroutes to thibautprobst.fr (54.230.112.104) with 30 hops max (1 packets per hop) on ICMP, UDP port 33434 and TCP port 33434 with a timeout of 2.0s
thibautprobst.fr (54.230.112.104) reached in 21 hops
Hop 1: 192.168.41.191 - ICMP: 39.92ms, UDP: 39.83ms, TCP: 39.59ms
Hop 2: 255.0.0.0 - ICMP: 57.34ms, UDP: 57.2ms, TCP: 56.93ms
Hop 3: * * * * * * * * - ICMP, UDP and TCP
Hop 4: 255.0.0.1 - ICMP: 160.43ms, UDP: 160.27ms, TCP: 159.69ms
Hop 5: 255.0.0.2 - ICMP: 76.01ms
255.0.0.4 - UDP: 132.48ms, TCP: 132.17ms
Hop 6: 255.0.0.3 - ICMP: 85.11ms
10.216.10.65 - UDP: 169.85ms
Hop 7: 255.0.0.4 - ICMP: 68.35ms
81.253.184.106 (ae31-760.ngesevir01.rbci.orange.net) - UDP: 150.83ms
Hop 8: 193.251.110.185 (ae31-0.nclyo201.rbci.orange.net) - UDP: 106.85ms
Hop 9: 193.252.101.145 (ae41-0.nilyo101.rbci.orange.net) - UDP: 108.51ms
Hop 10: 81.253.184.86 - UDP: 146.15ms
193.252.101.65 (ae58-0.nilyo101.rbci.orange.net) - TCP: 127.85ms
Hop 11: 193.251.255.186 (amazon-34.gw.opentransit.net) - UDP: 169.31ms
81.253.184.86 - TCP: 137.89ms
Hop 12: 81.253.184.86 - ICMP: 119.5ms
Hop 13: * * * * * * * * - ICMP, UDP and TCP
Hop 14: * * * * * * * * - ICMP, UDP and TCP
Hop 15: * * * * * * * * - ICMP, UDP and TCP
Hop 16: * * * * * * * * - ICMP, UDP and TCP
Hop 17: * * * * * * * * - ICMP, UDP and TCP
Hop 18: * * * * * * * * - ICMP, UDP and TCP
Hop 19: * * * * * * * * - ICMP, UDP and TCP
Hop 20: * * * * * * * * - ICMP, UDP and TCP
Hop 21: 54.230.112.104 (server-54-230-112-104.mrs52.r.cloudfront.net) - ICMP: 138.65ms
Conclusion
Throughout this article, we have explored flyingroutes, an alternative to traceroute that allows parallel requests to be sent in order to more quickly determine the path and nodes taken to reach a target on the network. Once again, I encourage your healthy skepticism, as there are many other good diagnostic tools similar to traceroute or flyingroutes that will likely meet your needs. I can recommend tools like tracepath, mtr, mtraceroute or Trippy. By the way, I encourage you to read this excellent article that introduces some of these tools.
Sources
- GitHub - thibaut-probst - flyingroutes: https://github.com/thibaut-probst/flyingroutes. Consulted on 16/12/2022.
- man7.org - traceroute(8) — Linux manual page: https://man7.org/linux/man-pages/man8/traceroute.8.html. Consulted on 16/12/2022.
- Wikipedia - traceroute: https://en.wikipedia.org/wiki/Traceroute. Consulted on 16/12/2022.
- InetDaemon.Com - How Traceroute Works: https://www.inetdaemon.com/tutorials/troubleshooting/tools/traceroute/definition.shtml. Consulted on 16/12/2022.
- Wikipedia - Multithreading: https://fr.wikipedia.org/wiki/Multithreading. Consulted on 16/12/2022.
- man7.org - tracepath(8) — Linux manual page: https://man7.org/linux/man-pages/man8/tracepath.8.html. Consulted on 16/12/2022.
- BitWizard B.V. - MTR: https://www.bitwizard.nl/mtr/. Consulted on 16/12/2022.
- GitHub - rwhalb - mtraceroute: https://github.com/rwhalb/mtraceroute. Consulted on 16/12/2022.
- GitHub - fujiapple852 - trippy:https://github.com/fujiapple852/trippy. Consulted on 13/09/2024.
- jmanteau - The Facets of Ping: https://jmanteau.fr/posts/the-facets-of-ping/. Consulted on 16/12/2022.