Here is an example of a toy traceroute using `ping` on Linux:

    $ for ttl in {1..30}; do ping -4 -c 1 -t $ttl example.com && break; done | grep -i from | nl -s ' ' -w 2
     1 From router1-lon.linode.com (212.111.33.229) icmp_seq=1 Time to live exceeded
     2 From if-0-1-0-0-0.gw1.lon1.gb.linode.com (109.74.207.10) icmp_seq=1 Time to live exceeded
     3 From be5787.rcr51.lon10.atlas.cogentco.com (204.68.252.58) icmp_seq=1 Time to live exceeded
     4 From be2589.ccr41.lon13.atlas.cogentco.com (154.54.59.37) icmp_seq=1 Time to live exceeded
     5 From be2099.ccr31.bos01.atlas.cogentco.com (154.54.82.34) icmp_seq=1 Time to live exceeded
     6 From verizondms.bos01.atlas.cogentco.com (154.54.11.54) icmp_seq=1 Time to live exceeded
     7 From ae-66.core1.bsa.edgecastcdn.net (152.195.233.129) icmp_seq=1 Time to live exceeded
     8 64 bytes from 93.184.216.34 (93.184.216.34): icmp_seq=1 ttl=57 time=63.0 ms

The output above was obtained on a Debian GNU/Linux 11.2 (bullseye) system. The above loop sends multiple ICMP echo requests with different Time to Live (TTL) values to reach the host for `example.com`. The TTL occurs as an 8-bit field in the Internet Protocol (IP) header of each packet. It is the 9th octet in an IPv4 header and the 8th octet in an IPv6 header.

When a router on the path to the destination receives a packet, it decrements the TTL value in the IP header by one. If the TTL value becomes 0 after the decrement operation, the router responds with a "time-to-live exceeded" ICMP message. Thus an echo request with TTL value set to 1 gets us an ICMP "time-to-live exceeded" message from the first router in the path, the next echo request with TTL value 2 gets us a similar ICMP message from the second router in the path and so on. The traceroute is complete when we receive a successful ICMP echo reply.

For comparison, here is the output of the actual `traceroute` command:

    $ traceroute example.com
    traceroute to example.com (93.184.216.34), 30 hops max, 60 byte packets
     1  router1-lon.linode.com (212.111.33.229)  0.602 ms  1.202 ms  1.326 ms
     2  if-0-1-0-1-0.gw1.lon1.gb.linode.com (109.74.207.14)  0.502 ms if-11-1-0-0-0.gw2.lon1.gb.linode.com (109.74.207.26)  0.401 ms if-11-0-0-0-0.gw2.lon1.gb.linode.com (109.74.207.30)  0.379 ms
     3  be5787.rcr51.lon10.atlas.cogentco.com (204.68.252.58)  0.573 ms  0.563 ms  0.566 ms
     4  be2589.ccr41.lon13.atlas.cogentco.com (154.54.59.37)  1.271 ms  1.311 ms ldn-bb1-link.ip.twelve99.net (62.115.122.188)  1.400 ms
     5  be2099.ccr31.bos01.atlas.cogentco.com (154.54.82.34)  63.511 ms  63.540 ms nyk-bb1-link.ip.twelve99.net (62.115.112.244)  73.397 ms
     6  nyk-b1-link.ip.twelve99.net (62.115.135.131)  70.113 ms verizondms.bos01.atlas.cogentco.com (154.54.11.54)  63.657 ms nyk-b1-link.ip.twelve99.net (62.115.135.131)  70.190 ms
     7  ae-66.core1.bsa.edgecastcdn.net (152.195.233.129)  63.535 ms edgecast-ic317659-nyk-b6.ip.twelve99-cust.net (62.115.147.199)  77.802 ms ae-66.core1.bsa.edgecastcdn.net (152.195.233.129)  63.582 ms
     8  93.184.216.34 (93.184.216.34)  62.895 ms ae-71.core1.nyb.edgecastcdn.net (152.195.69.139)  72.312 ms ae-70.core1.nyb.edgecastcdn.net (152.195.68.141)  69.419 ms
     9  93.184.216.34 (93.184.216.34)  70.827 ms  62.896 ms  73.342 ms

Each line in the output above corresponds to a hop. Each line shows one or more addresses. A maximum of three addresses can be seen in the result for each hop. That is because there are multiple paths to the destination and the `traceroute` command sends 3 UDP probe packets by default for each hop. In this manner, it ends up discovering multiple routes to the destination. It is worth noting here that by default `traceroute` sends UDP packets, not ICMP echo requests, with different TTL values as probe packets. But the route discovery mechanism remains the same. After sending each probe packet, it waits for ICMP "time-to-live exceeded messages" from the routers that fall in the path to the destination.

By comparing the two outputs above, we can see that the route found by the toy traceroute using `ping` is one of the several routes found by the `traceroute` command.

For those on macOS, the `ping` command options need to be modified as follows:

    for ttl in {1..30}; do ping -c 1 -t 1 -m $ttl example.com && break; done | grep -i from | nl -s ' ' -w 2

On macOS, the `-t` option of the `ping` command specifies a timeout (not IP TTL) that prevents it from waiting for too long for a successful echo reply which we don't expect to receive. Further, on macOS, the `-m` option of the `ping` command specifies the IP TTL.