Packet Time-to-Live and Trace Route
Table of Content
- Introduction
- Trace Route
- Appendix I. Guessing Hosting Locations from IP Addresses
- Appendix 2. Mapping Routes
Introduction
IP packet has a TTL field where TTL is an acronym for time-to-live. A forwarding node decrements the TTL value in the packet and then forward it, or drop it when the TTL value becomes 0 and reply the source with a ICMP Time-Exceeded message.
Trace Route
Knowning how a router decrements the TTL value and response to a packet with the 0 TTL value, we can design a trace route program to figure out routers between a source and a destination node. You can actually implement it using with a few different protocols. If you are sending UDP datagrams, you have a UDP traceroute; if you are sending out a TCP SYN segments, you have a TCP traceroute; and if you are sending out ICMP packet, you have a ICMP traceroute.
Below is an ICMP traceroute program in Python using ScaPy. Let’s call this
program icmptracert.py
icmptracert.py
import sys
from scapy.layers.inet import IP
from scapy.layers.inet import ICMP
from scapy.sendrecv import sr1
from socket import gethostbyname
def print_route(hops, reply):
nonemsg = '*****None******'
unreachmsg = '*****None******'
hopmsg = 'hops'
typemsg = 'ICMP msg type '
if hops == 1:
hopmsg = 'hop'
if reply is None:
src = '*****None******'
rtype = 'OOPS'
typemsg = ''
elif reply.type == 3:
src = '**Unreachable**'
rtype = reply.type
else:
src = reply.src
rtype = reply.type
print(f'{hops:2d} {hopmsg:4} away from {src:15} [{typemsg}{rtype:2}]')
if not reply is None and reply.type == 0:
print ('Desitnation reached')
def icmp_trace_route(dst):
dstaddr = gethostbyname(dst)
noops = 0
print(f'Traceroute to {dst} at {dstaddr}:')
for i in range(1, 65): # try 1 hops to 64 hops
# prepare a ICMP packet (what type of ICMP message is it)?
pkt = IP(dst=dstaddr, ttl=i) / ICMP()
# Send the packet and get a reply
reply = sr1(pkt, verbose=0, timeout=5)
if reply is None:
noops = noops + 1
print_route(i, reply)
if noops >= 5:
break
elif reply.type == 0:
print_route(i, reply)
break
elif reply.type == 3: # Destination Unreachable (RFC 792)
print_route(i, reply)
elif reply.type == 11: # Time Exceeded (RFC 792)
print_route(i, reply)
else: # Hi friend, How are you configured? Why am I getting this?
print_route(i, reply)
def main(argv):
if len(argv) < 2:
print('Usage: ' + argv[0] + ' destination')
sys.exit(1)
dst = sys.argv[1]
nodes = icmp_trace_route(dst)
if __name__=='__main__':
main(sys.argv)
Running icmptracert.py
Below are some examples.
- Trace route to www.brooklyn.cuny.edu
sudo python icmptracert.py www.brooklyn.cuny.edu
- Trace route to www.google.com
sudo python icmptracert.py www.google.com
- Trace route to www.facebook.com
sudo python icmptracert.py www.facebook.com
Exercise and Exploration
- Trace route to a few of your favorite web sites. Is there anything notable that you observe?
- How do we revise the program to do a UDP tracerroute instead? What have you tried? What do you observe from your exploration?
Appendix I. Guessing Hosting Locations from IP Addresses
Knowing location of a host is useful to an array of application, such as, positioning you to a nearest geographical point of interest, like a supermarket or a coffee shop when you visit the supermarket’s website or use the coffee shop’s mobile app. There have emerged vendors providing service to determine host locations of IP addresses. A host’s geographical location and its IP address assignment are dynamic and proprietary. The locations have accuracy varying across a number of dimensions, such as, ISP and countries. So, unless you are the ISP or the ISP shares with you the locations, these are a best effort guess.
The following example is to guess the geographical locations of the IP addresses returned from the traceroute experiments, which may provide you with an insight on the scale of the global Internet.
The example uses ipgeolocation.io’s RESTful
Web service
to guess the locations of the IP addresses. So, your first step is to sign up
for a free developer account for an API KEY at
https://ipgeolocation.io/pricing.html.
With the API KEY, one can issue 1,000 requests a day at present. To
run the following program replace the string YOUR_API_KEY
with your
actually API KEY.
It is worthy of comparing the get_geo_loc()
function in the following program
with the main()
function in helloclient.py, which reveals that that we write
the program by revising the helloclient.py program since
the following program is actually a Web client to a RESTful Web service.
ip2loc.py
from http import client as httpclient
import json
import sys
HOST = 'api.ipgeolocation.io'
API_KEY = 'YOUR_API_KEY'
def build_request(ip):
request = '/ipgeo?apiKey=' + API_KEY + '&ip=' + ip + '&output=json'
return request
def get_geo_loc(ip):
request = build_request(ip);
conn = httpclient.HTTPSConnection(HOST)
conn.request('GET', request)
response = conn.getresponse()
if response.status != 200:
location = (False, response.status, json.loads(response.read()))
else:
location = (True, response.status, json.loads(response.read()))
conn.close()
return location
def main():
ips = [line.strip() for line in sys.stdin]
for ip in ips:
success,_,location = get_geo_loc(ip)
if not success:
print(ip, location['message'])
else:
print(ip, location['latitude'], location['longitude'])
if __name__ == "__main__":
# execute only if run as a script
main()
Running ip2loc.py
The ip2loc.py takes in a list of IP addresses one IP address a line from the Stadndard Input while the icmptracert.py prints out the routes on the Standard Output. Given these, we run icmptracert.py and ip2loc.py in a pipelined fashion as in the following example where we trace route to www.facebook.com,
python icmptracert.py www.facebook.com > facebook_route.txt
grep "hop" facebook_route.txt | grep hop tr.txt | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]" > facebook_route_ip.txt
python ip2loc.py < facebook_route_ip.txt > facebook_route_loc.txt
You should take a look at the output files, like facebook_route_loc.txt.
Appendix 2. Mapping Routes
Next step is to put the routes on the map if we know each node’s latitutde and
longitude. Python has a few packages to plot maps. The loc2gmap.py is to plot
the route on a map generated using the gmplot
package, a Python wrapper of
the Google Map API, a JavaScript API.
Preparing Environment
On the virtual machine you’ve beening doing the trace route experiment and
getting the route’s locations, install pip3
and gmplot
by following
the steps below,
sudo apt-get install --no-install-recommends python3-pip
sudo pip3 install gmplot --upgrade
loc2gmap.py
import sys
from gmplot import GoogleMapPlotter
def get_ip_locs():
locs = [line.split() for line in sys.stdin]
lat_list = [float(loc[1]) for loc in locs]
lon_list = [float(loc[2]) for loc in locs]
lbl_list = [loc[0] for loc in locs]
lat_center = sum(lat_list)/len(lat_list)
lon_center = sum(lon_list)/len(lon_list)
return lat_list,lon_list,lbl_list,lat_center,lon_center
def main(map_html_fn):
lat_list,lon_list,lbl_list,lat_center,lon_center = get_ip_locs()
gmap = GoogleMapPlotter(lat_center, lon_center, 13)
gmap.scatter(lat_list, lon_list, '#3B0B39', size=200, marker=False )
for lat,lon,lbl in zip(lat_list,lon_list,lbl_list):
gmap.text(lat, lon, color='#3B0B39', text=lbl)
gmap.plot(lat_list, lon_list, 'cornflowerblue', edge_width = 2.5)
gmap.draw(map_html_fn)
if __name__ == '__main__':
if len(sys.argv) < 2:
print('Usage: loc2gmap.py YOUR_MAP_NAME.html')
sys.exit(1)
main(sys.argv[1])
Running loc2gmap.py and Viewing the Map
- First, we generate the map using the output obtained by
running ip2loc.py in Appendix I as follows,
grep -v bogon facebook_route_loc.txt | python loc2gmap.py facebook_route_gmap.html
where facebook_route_gmap.html contains the map.
- How do we view the map? We rely on Python’s built-in Web server. First,
take a note of the IP address of the virtual machine,
ip address show
Let’s assume the IP address is 192.168.56.101. Then we proceed to the next step.
- In the directory where the map html file is, run
python -m http.server
Below is an example including output,
brooklyn@midwood:~$ python -m http.server Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
which shows that the Web server listens to incoming connections at end point
0.0.0.0:8000
. - Finally, on the host (e.g., Windows or Mac), point your favorite Web browser, like Firefox, Chrome, or Safari to something like http://192.168.56.101/facebook_route_gmap.html, where the host in the URL is 192.168.56.101 because it is the IP address of the virtual machine we took note of, the map file is the file we generated using loc2gmap.py.
Ignore whatever Google Map complains. Zoom out or in the map. Do you see anything interesting?