/ raspberry pi

Monitor your local network with Raspberry Pi

Google recently released its own WiFi router, called OnHub. It seems like a game-changer product that comes with some innovative and useful features. The most interesting one is that you can keep an eye on the devices that are connected at your local network and even monitor their WiFi usage live. That's amazing!

I immediately wondered whether you can make something similar without spending 200$. By using only your current WiFi router that's surely impossible. So let's bring the Raspberry Pi in the game. Raspberry Pi costs about 35$ so it's a cheap solution for you to experiment with. It is a normal computer and its usages don't stop there, so why not give it a try?

How can you take advantage of the Pi to improve your home network's monitoring?

Can you monitor live usage of WiFi?

Surely no. Simply because WiFi is managed by the router and most of the routers out there don't provide you with such information or statistics.

Can you monitor the connected devices?

That we can do. We can either use the WiFi router as our DHCP server and acquire all information about the clients from an interface it provides (for example ssh or telnet, a difficult scenario though), or we can use the Raspberry Pi as our DHCP server and setup a web server (pretty easy and cross-device) that will beautifully print the client list of our local network. This is what we are going to do.

The goal

To be more clear, let's take a look at the final result so you have a better picture about what we are going to implement.

Requirements

  1. A WiFi router (TP-Link TD-W8970 in my case)
  2. A Raspberry Pi
  3. Some basic UNIX and network knowledge

The process

Setup/connection:
Before we start we must discuss about the connections of the setup. The Raspberry Pi will serve as the DHCP server of the network. In order for the network to be functional we must have the Pi always connected with the router and always running the DHCP server software. If you have a modern WiFi router it's highly possible that it has USB port(s) on the back, and therefore it can power up the Pi! Very convenient. Also connect your Pi to to the router using an ethernet cable. You can also use an external power supply or a WiFi adapter instead, but it will add extra complexity to your system, so try to avoid it.

WiFi router preperation: Most of the modern home WiFi routers have a DHCP server enabled by default. In order to continue you have to configure it to operate in relay mode. Below is how this looks like in my occasion:

Note: 192.168.1.107 is the static IP that we want to assign to the DHCP server (the Pi).

DHCP server setup: Login into your Raspberry Pi and install isc-dhcp-server

sudo apt-get install isc-dhcp-server

In order for the DHCP server to be functional, you will need to run some configuration first.
Edit your /etc/network/interfaces file. Assuming you have connected your Pi via ethernet, your network interface that connects to the WiFi will be eth0. Make sure you add the following lines to the file:

iface eth0 inet dhcp
allow-hotplug eth0
iface eth0 inet static
address 192.168.1.107
netmask 255.255.255.0
gateway 192.168.1.1

Note: We chose to use the range 192.168.1.0/8 as our local network, with 192.168.1.107 being Pi's IP and 192.168.1.1 being the WiFi router's one.

It's time to configure to DHCP server. Make sure the /etc/default/isc-dhcp-server file has the following line

INTERFACES="eth0"

The basic DHCP server configuration happens in the /etc/dhcp/dhcpd.conf file. Add the following lines and then let's explain them.

subnet 192.168.1.0 netmask 255.255.255.0 {
        range 192.168.1.100 192.168.1.199; # Range = 100 devices
        option broadcast-address 192.168.1.255;
        option routers 192.168.1.1;
        default-lease-time 1200;
        max-lease-time 2400;
        option domain-name "local";
        option domain-name-servers 8.8.8.8, 8.8.4.4;

        host router { #WiFi router
                hardware ethernet E8:94:F6:0F:XX:XX; #Router's MAC address
                fixed-address 192.168.1.1; #Router's static IP
        }
        host raspberry-pi { #Pi, the DHCP server
                hardware ethernet E8:94:F6:21:XX:XX; #Pi's MAC address
                fixed-address 192.168.1.107; #Use the same IP you set on your
                                             #router's DHCP relay configuration
        }
}

Let's explain some things. The local network IPs will have the following format 192.168.1.XXX, while 192.168.1.1 is the WiFi router (static IP based on MAC address) and 192.168.1.107 is the Pi's IP (same as you declared on your WiFi router's DHCP configuration page). All the clients will have IPs within the 192.168.1.(100-199) range. We also used Google DNS.

Note: In order for the changes to take effect, you have to restart both the router and the Pi.

The web interface: We now have a working DHCP server on Raspberry Pi, so let's move on and build the web interface that will simplify things and let us have a quick and clean view to the home network's clients.

All the information we need is located in the /var/lib/dhcp/dhcpd.leases file. So the first problem we have to deal with is how we are going to read it, parse it, reformat it and pass it to our web page. Node.js is probably the best solution to achieve this, not only because it can read the file we are interested in, but it also comes with an extension that handles the parsing! This is very helpful, because you don't want to waste time on file parsing at the moment, do you?

Install node.js by typing

sudo apt-get install --yes nodejs

...and also the DHCP leases parser

npm install dhcpd-leases

We are ready to write the script that will parse the DHCP leases file and return the clients information in a JSON file that we will use later in our web page to display them. Create a new file (at the same directory you installed the dhcp-leases package above) and paste the following code. This will be our nodejs server.


var http = require('http');
var fs = require('fs');
var dhcpdleases = require('dhcpd-leases');
var server = http.createServer(function(req, res) {
    fs.readFile( '/var/lib/dhcp/dhcpd.leases', function(error, data) {
        if (error) {
            res.setHeader('Content-Type', 'application/json');
            res.send(JSON.stringify({
                'error':'Server error',
                'message':error
            }));
        }
        else {
            var leases = fs.readFileSync('/var/lib/dhcp/dhcpd.leases', 'utf-8');
            var data = dhcpdleases(leases);
            res.setHeader('Content-Type', 'application/json');
            res.end(JSON.stringify(data));
        }
    });
});
server.listen(8000);

The nodejs server will run at port 8000 as we chose above. Let's say you named the script server.js. Run the server by typing

nodejs server.js

I suggest you use a screen session so you can logout while the server keeps running. To do that, simply type screen, run the server and then press Ctrl+A+D to exit the screen session. To restore your last screen session and stop the server type screen -rx.

If everything has gone according to the plan, you can head to 192.168.1.107:8000 to your browser. You should see a JSON file.

Now it's time to setup the web page. It will be static since no further back-end stuff is needed. Simply install nginx by typing

sudo apt-get install nginx

Let's edit the /etc/nginx/sites-enabled/default file and add the following lines (where USERNAME is your Pi username):

server {
		[...]
        location /dhcp {
                alias /home/USERNAME/html/dhcp/;
                allow all;
        }
        location /dhcp/getconf/ {
                proxy_pass http://127.0.0.1:8000;
        }
        location /dhcp/getmac/ {
                proxy_pass http://api.macvendors.com/;
        }
        [...]
}

The first location configuration tells nginx where your web page contents for the /dhcp path are located. The second location configuration is required so we don't have to deal with CORS and we are able to get the JSON file of the DHCP leases from the /dhcp/getconf/ path. The third one, is used to get the MAC address manufacturer information from macvendors.com by applying the mac address to the /dhcp/getmac/ path. All those sound pretty useful, right? Just another step, and we are ready. Let's continue the web page that will get the JSON file and display it beautifully as HTML.

Create an index.html file within /home/USERNAME/html/dhcp/ folder and the paste the following code:

<!doctype html>
<html>
<head>
<title>DHCP info</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<style>
.mono { color: #666; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; }
.c { color: #000; }
h4 { font-weight: 300; color: #555; }
</style>
</head>
<body>
<div class="panel panel-default">
<div class="panel-heading">
<h4>DHCP information. There are <strong class="c"><span id="devices">0</span> devices</strong> connected now.</h4>
</div>
<table class="table">
<thead>
<tr>
<th>IP</th>
<th>Hostname</th>
<th>MAC address</th>
<th>MAC manufacturer</th>
</tr>
</thead>
<tbody id="clients">
</tbody>
</table>
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script>
var known_macs = {};
var update = function(){
$.get( 'getconf/', function(data) {
var leases = data;
// Remove previous info
$('#clients').html('');
var total_devices = 0;
for (ip in data) {
var ipinfo = data[ip];
$('#clients').append('<tr>'+
'<td>'+ip+'</td>'+
'<td>'+(ipinfo['client-hostname']||'-')+'</td>'+
'<td class="mono">'+(ipinfo['hardware ethernet']||'-').toUpperCase()+'</td>'+
'<td class="mac-manufacturer" data-mac="'+(ipinfo['hardware ethernet']||'-').toUpperCase()+'"></td>'+
'</tr>');
total_devices += (ipinfo['client-hostname']) ? 1 : 0;
}
$('#devices').html(total_devices);
$('.mac-manufacturer').each(function(){
var elem = $(this);
var mac = elem.attr('data-mac');
if (typeof known_macs[mac] != 'undefined')
elem.html(known_macs[mac]);
else
$.get('getmac/'+mac, function(data){
elem.html(data);
known_macs[mac] = data;
});
});
});
};
$(document).ready(function(){
update();
var t = setInterval(update, 30000);
});
</script>
</body>
</html>

That's it! We are ready. Simply head to 192.168.1.107/dhcp/ to your browser and you will see your home network's clients, quickly and organized. No login to WiFi routers, no searching. Just visit a web page.

Future extensions

If you want to go a step further, there are plenty of opportunities for you. First of all you can take advantage of the unused-so-far fields of the JSON file that contains the DHCP leases. The "binding state" field tells you if the client is currently active or its lease has expired. You can practice your skills by adding an extra "active" column indicating whether the client is currently connected.

If you are willing to go even further, you can always extend your back-end features. You can extend your node.js server to modify the isc-dhcp-server configuration files and take full advantage of the DHCP protocol, or even setup email notifications whenever a new client is discovered. Just use your imagination and creativity and make something new and cool.