I recorded an episode of HPR about a script that I wrote to make my life a little easier. The show is hpr1654 :: Using AS numbers to identify where you are on the Internet if you want to listen along.
My “itch”
I have a laptop and I want it to use different configurations depending on where I am. If I’m on wifi at home, I don’t want my NAS mounted, but if I’m on a wired connection I do. If I’m at work I want to connect to various servers there. If I’m in the train I want to setup a vpn tunnel. You get the idea.
My solution to this was to approach it from the laptop and go out. So to look around and see what network I was on. There are a few ways to approach this, you could look at your IP address, the arp tables, try and ping a known server in each location. The issue with looking at an IP address is that most networks use Private Networks. Very soon you will find that the wifi coffee shop happens to have picked the same range as you use at home and now your laptop is trying to backup to their cash register.
To get around this I tried other solutions such as looking at the MAC address of the default gateway using IP Route and Arp, but that requires a lot of maintenance as devices change a lot.
$ arp -n | grep $(/sbin/ip route | awk '/default/ { print $3 }') | awk '{print $3}'
aa:bb:cc:dd:ee:ff
The next option was to try and ping known servers, but that resulted in a lot of delays as the pings will by definition need to time out, as you run down the list of possible places you are.
Then I was thinking that I’m approaching this problem from the wrong angle. Why not start with my public IP address range, which has to be unique, and work back from there to my laptop. There are a lot of services out there that provide look up services. Some I have used in the past are
Now even Google gives back your IP address if you type in “my ip address” into the search bar. Rather than using those services I just set up a small php file on my own server that returns the public IP address of your connection. So even if your home and coffee shop happen to have the same 192.168.1.0/24 range, they will have different public IP address ranges.
<?php
$ip = $_SERVER['REMOTE_ADDR'];
print "$ip";
?>
From there I was planning on maintaining a look-up table of public IP addresses, along the lines of the GeoIP tools developed by MaxMind. They provide the GeoLite Country and GeoLite City databases under a OPEN DATA LICENSE, which looks to me like a modified Apache License (IANAL). They provide a C library under the LGPL.
For those not familiar with Geolocation based on IP address, it’s the technology that maps your Public IP address to a physical location. This is what blocks the BBC iplayer website outside of the UK, or presents a cookie warning within the EU, or stops everyone else in the world watching US TV websites. For most applications the location is very coarse, based on information from the regional Internet registries. Once you get past country level you need to start investing serious money to get the data and so you can expect to pay for the more granular information.
The more detailed you get the more concerned you need to be about privacy. The location for most peoples home connection is mapped to the location of their Internet Providers head office. After checking my ip address location on http://www.iplocation.net/, of the six databases queried four put me in the head office of my ISP, one had the right town and another had me the other side of the country. So for a website that needs to perform an action based on the country of origin IP address it is quite useful but for my personal use case, it wasn’t going to help me a lot.
# geoiplookup 8.8.8.8
GeoIP Country Edition: US, United States
That was until I ran the exact same command on Fedora.
# geoiplookup 8.8.8.8
GeoIP Country Edition: US, United States
GeoIP ASNum Edition: AS15169 Google Inc.
The first line is the same but what’s this about ASNum ? It’s not mentioned in the man page, but suffice to say they are very, very important for how the Internet works.
From WikiPedia: Autonomous System (Internet)
ISP must have an officially registered autonomous system number (ASN). A unique ASN is allocated to each AS for use in BGP routing. AS numbers are important because the ASN uniquely identifies each network on the Internet.
So what that is saying is that every network in the Inter(connected)Net(work), must have it’s own unique AS Number. So my home ISP will have a different AS Number, from my local coffee shop, from my office network, etc. It actually goes even further than that. Say you have the same provider for your home Internet and mobile Internet. Even though they might be using the same 10.0.0.0/8 ranges for all their networks, they will more than likely route between the private networks using public IP Addresses, and that means different, unique AS Number. Your mileage may vary on this, but for me it works out very well indeed.
It’s already installed on Fedora (yum install GeoIP), so to install the application on Debian/Ubuntu type:
aptitude install geoip-bin
This will drop the IPv4 (GeoIP.dat)and IPv6 (GeoIPv6.dat) databases into the directory /usr/share/GeoIP/. Your package manager will not update the databases for you, although there is a Fedora package GeoIP-update* to schedule a cron job it only updates the GeoLiteCity.dat file. Here is the script I use to update all the databases:
# vi /usr/local/bin/geoip-update.bash
Paste in the following code:
[code language=”bash” wraplines=”true”]
#!/bin/bash
for database in http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz http://geolite.maxmind.com/download/geoip/database/GeoIPv6.dat.gz http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz http://geolite.maxmind.com/download/geoip/database/GeoLiteCityv6-beta/GeoLiteCityv6.dat.gz http://download.maxmind.com/download/geoip/database/asnum/GeoIPASNum.dat.gz http://download.maxmind.com/download/geoip/database/asnum/GeoIPASNumv6.dat.gz
do
wget "$database" -O – | gunzip -c > /usr/share/GeoIP/$(basename "$database" .gz)
done
[/code]
Make the script executable
# chmod +x /usr/local/bin/geoip-update.bash
Then run it and check that you have new files in /usr/share/GeoIP to be sure it works. Finally all that’s left to do is to install it into cron. (Thanks James Wald)
# Minute Hour Day of Month Month Day of Week Command
# (0-59) (0-23) (1-31) (1-12 or Jan-Dec) (0-6 or Sun-Sat)
0 12 * * Mon /usr/local/bin/geoip-update.bash > /tmp/geoip-update.bash 2>&1
I have modified my mapping script so that it combines the location and the connection type. It first does a quick check to see if there is an Internet connection and will time out after 2 seconds.
wget --timeout=2 http://www.example.com/uptime.txt -O -
I already have this file on my server for remote monitoring, so it makes sense to reuse it. The file contains the word “success” and if that is not returned then you don’t have any Internet connection. My server could also be down but that would be a bigger problem for me at least.
The next part gets the Public IP Address and then uses it to find the AS Number
geoiplookup "8.8.8.8" | awk '/GeoIP ASNum Edition/ {print $4}'
So that’s all I need to find out my position on the Internet, but I also want to know what type of connection I’m using. For example, when I use a usb network theathering connection to my phone it displays as a wired connection when in fact it should be “wireless”. Once I have found both the location and the connection type, I then combine them with an underscore, and use a case statement to run the different commands.
Here is a finalized script:
[code language=”bash”]
#!/bin/bash
result="$(wget –timeout=2 http://www.example.com/uptime.txt -O – 2>/dev/null)"
if [ "${result}" != "success" ]
then
echo "No connection to www.example.com found"
exit 1
else
myip="$(wget –timeout=2 http://www.example.com/whatismyip.php -O – 2>/dev/null)"
asnum=$(geoiplookup "${myip}" | grep ‘GeoIP ASNum Edition: ‘ | awk ‘{print $4}’ )
case "${asnum}" in
AS1234)
location="home"
;;
AS2222)
location="work"
;;
AS3333)
location="roaming"
;;
AS5555)
location="roaming"
;;
*)
echo "No location found for AS Number \"${asnum}\""
exit 2
;;
esac
interface=$(route | awk ‘/default/ {print $(NF)}’)
fi
if [ "$( iwconfig 2>&1 ${interface} | grep ‘ESSID’ | wc -l )" -eq 1 ] || [ $(echo ${interface} | grep ppp | wc -l ) -eq 1 ]
then
type="wireless"
essid=$( iwconfig 2>&1 ${interface} | awk -F ‘"’ ‘{print $2}’)
else
type="wired"
fi
echo "Connection Found: $myip $asnum $location $interface $type $essid"
case "${location}_${type}" in
work_wired)
echo "Work Network"
;;
work_wireless)
echo "Work Wireless Network"
;;
roaming_wireless)
echo "Mobile Network"
;;
home_wired)
echo "Home Wired Network"
;;
home_wireless)
echo "Home Wireless Network"
;;
*)
echo "No custom configuration applied"
;;
esac
[/code]