Network Monitor

LAN security script scans for connected devices

3 minute read

After getting a QNAP NAS, I installed a network monitor agent just to give it a try. But after my trial expired I wasn’t ready to pay for it. Domotz is a cool product but I knew I could write something that accomplished the same goal.

My Python script scans for hosts periodically using Nmap, notifies me when a new device has connected, and saves MAC address, vendor, IP address, and connection timestamps to a database on my NAS.

The Goals

The main objectives of this project were to:

  • Get notifications when a new device connects to my network.
  • Be able to diagnose device connectivity problems.
  • Trigger home automation based on device presence.

I track devices by MAC address since IP is dynamic for many devices. Whenever the script runs, it updates the last seen at timestamp for each known device. If the device’s database record indicates it’s not connected then it creates a new row.

If there’s no record for the seen MAC address, we create a new record and send a notification to IFTTT (which sends me a Telegram message with the scan data).

The Code

Here’s the main script that’s run by a cronjob:

nmap -sn $1 | python3 run.py

The Python script receives all of the online hosts by reading Nmap’s piped output. Then it queries the database for every unique MAC address and iterates through the seen devices.

There are three conditions to handle:

  1. We’ve never seen this device before.
  2. We know this device but it’s been offline.
  3. We know it and it’s still online.

Here’s some pseudo code for the Python script:

for device in scan_data:
    prev_event = known_devices[device['mac_address']]

    if not prev_event:
        write_to_database(device)
        send_notification(device)
    else if not prev_event['seen_recently']:
        write_to_database(device)
    else:
        update_seen_timestamp(prev_event)

And here’s the SQL schema:

CREATE TABLE nm_device_events (
  id INT NOT NULL auto_increment PRIMARY KEY,
  mac VARCHAR(17) NOT NULL,
  vendor VARCHAR(64),
  ip VARCHAR(15) NOT NULL,
  connected_at timestamp DEFAULT 0,
  last_seen_at timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

If you want to see the rest of the code check it out on Github!

Some process notes

Reducing Noise

The first couple iterations of this script had an online field that was flipped off if the device wasn’t seen in a given scan. The next time it showed up in a scan the script would write a new row. This approach ended up with a lot of noise in the database due to the fact that many devices go into sleep mode and periodically wake up, reconnect, then fall back asleep. I ended up increasing the seen_recently window to be 45 minutes!

Notifications

I’m using IFTTT’s Maker webhooks to send myself notifications through Telegram. This way I can

Future Improvements

There are quite a few things that would make this project more useful!

Dashboard

I’m hoping to create a dashboard to view and manage the monitor.

Through the dashboard I’d be able to graph each device’s connectivity status with connected and last seen timestamps.

Manual Metadata

It would be nice to be able to manually enter some device metadata:

  • type (guest, IoT, or media)
  • aliases (Livingroom Chromecast, Raspberry Pi, or Mark’s phone)
  • groups (my laptop and phone, home automation devices, or important)

This would allow me to filter them on the dashboard or set specific notifications if an important device goes offline.

Presence Triggers

Home automation devices would really benefit from an easy way to detect if I’m home or away. Right now I use IFTTT to notify specific applications based on phone geo-fencing and network connection but this would allow me to create rules for other devices and cut out the middleman.