1
1
Fork 0
mirror of https://github.com/BillDietrich/lanwatch.git synced 2024-05-09 03:16:04 +02:00

add entry for local machine, get local name, set router description

This commit is contained in:
Bill Dietrich 2020-03-30 17:04:54 +02:00
parent 2fa73ec852
commit 8b783fee86
3 changed files with 96 additions and 54 deletions

View File

@ -1,10 +1,6 @@
# lanwatch
Report new devices that appear on LAN, and maintain an inventory of devices.
[IN PROTOTYPING STAGE; NOT DANGEROUS, BUT NOT READY FOR USE !!!]
![Do not use](http://4.bp.blogspot.com/-1lTbJMSPZaE/Tyu0eri0bOI/AAAAAAAAEP0/L6yk8jqGUwI/s1600/abnormal%2Bbrain.jpg "Do not use")
https://github.com/BillDietrich/lanwatch
---
@ -14,10 +10,10 @@ https://github.com/BillDietrich/lanwatch
### Copy the minimal files to disk
In the GitHub repo, click the "Clone or download" button, then click the "Download ZIP" button. Save the ZIP file to disk.
#### On Linux
Copy file lanwatch.py from the ZIP file to /usr/local/bin
Copy files lanwatch.py and lanwatch-MACVendors.csv from the ZIP file to /usr/local/bin
#### On Windows 10
Copy files lanwatch.cmd and lanwatch.py from the ZIP file to some folder.
Copy files lanwatch.cmd and lanwatch.py and lanwatch-MACVendors.csv from the ZIP file to some folder.
### Requires Python 3.3+
#### On Linux
@ -39,6 +35,11 @@ pip3 install scapy
pip3 install smbprotocol
```
If you want to do desktop notifications, you must also:
```bash
sudo -H pip3 install plyer
```
#### On Windows 10
Open windows command prompt: Win+X and then choose "Command Shell (as Administrator)".
@ -65,20 +66,39 @@ With Python installed:
## Quick-start to try lanwatch: run it manually
### On Linux command-line
1. Run the application:
```bash
sudo lanwatch.py
```
See desktop notifications.
2. See desktop notifications.
3. After the notifications stop (all current devices are found), kill the application and edit the /usr/local/bin/lanwatch.csv file to add information (such as host names and descriptions: e.g. ```Joe's laptop,HP Pavilion```). The file line format is ```MAC address,network chip vendor,host name,description```.
4. Run the application again.
5. Any time a new device appears, see a notification.
Technically, you could edit the /usr/local/bin/lanwatch.csv file while the application is running. But there is a chance that you could be editing the file when a new device appears, and the application would read and then write the same file you're editing, which would not be good. Best to stop the application when you want to edit the /usr/local/bin/lanwatch.csv file.
The /usr/local/bin/lanwatch-MACVendors.csv file is read only when the application is started. So it is safe for you to edit that file at any time, but changes will not be used until you stop and restart the application. The file line format is ```First half of MAC address,network chip vendor```.
### On Windows 10
Double-click on lanwatch.cmd file.
See notifications in "action center" at right end of system tray.
1. Double-click on lanwatch.cmd file.
2. See notifications in "action center" at right end of system tray.
3. After the notifications stop (all current devices are found), kill the application and edit the lanwatch.csv file to add information (such as host names and descriptions: e.g. ```Joe's laptop,HP Pavilion```). The file line format is ```MAC address,network chip vendor,host name,description```.
4. Run the application again.
5. Any time a new device appears, see a notification.
---
## Ways lanwatch can report IP address changes
## Ways lanwatch can report new devices
You can choose one or more of the following:
@ -118,13 +138,9 @@ For Win10, to see output, run Event Viewer application. Look in administrative
sudo lanwatch.py
```
Then try steps in the "Testing" section, below.
#### On Windows 10
Double-click on lanwatch.cmd file.
Then try steps in the "Testing" section, below.
### Run the program automatically
#### From a Linux systemd service started at system boot time
@ -134,11 +150,10 @@ sudo edit /usr/local/bin/lanwatch.py # to set gsUIChoice to "syslog".
sudo cp lanwatch.service /etc/systemd/system
```
After rebooting, on command-line do
After rebooting, when desired to see if there are any new devices, on command-line do
```bash
sudo journalctl | grep lanwatch
```
Then try steps in the "Testing" section, below, and check the journal again.
#### From a Windows 10 task started when you log in
@ -152,25 +167,23 @@ Then try steps in the "Testing" section, below, and check the journal again.
8. Save the task.
9. The task will appear in the list of Active Tasks (bottom of middle pane).
10. Log out and back in.
11. lanwatch should report an IP address change, in whatever way it's configured to report.
---
## Testing
1. After lanwatch.py starts (either via command-line or service), add a new device on the LAN.
11. lanwatch should report any new LAN devices, in whatever way it's configured to report.
---
## Limitations
* Tested only on Linux Mint 19.3 Cinnamon with 5.3 kernel, and Windows 10 Home.
* Tested only on Linux Mint 19.3 Cinnamon with 5.3 kernel.
* Tested only with IPv4, not IPv6.
* On Linux, tested only with strongSwan/IPsec to Windscribe VPN.
* On Win10, tested only without VPN.
* On Linux, tested only with strongSwan/IPsec to Windscribe VPN, and without VPN.
* Not tested on a LAN with no internet access.
* Requires Python 3.3 or greater.
* Can't guarantee that quick, transient device appear/disappear will be detected.
* Polls every 5 minutes, so a quick, transient device appear/disappear probably won't be detected.
* Doesn't get host names automatically.
## To-Do
* Desktop notifications don't work because of sudo.
* Automatically set host names of at least this machine and the router.
* Find a way to get host names automatically.
---

1
lanwatch-MACVendors.csv Normal file
View File

@ -0,0 +1 @@
f2:e8:ae,Broadcom
1 f2:e8:ae Broadcom

View File

@ -2,7 +2,7 @@
#--------------------------------------------------------------------------------------------------
# lanwatch.py Report new devices that appear on LAN, and maintain an inventory of devices.
# https://github.com/BillDietrich/lawatch
# https://github.com/BillDietrich/lanwatch
# If this is going to run at boot-time, put this file in the root filesystem
# (maybe in /usr/local/bin) instead of under /home, because /home may not
@ -33,17 +33,18 @@
# edit these to change the behavior of the app
gsIPRange = '192.168.0.0/24'
gsAccessType = 'HTTP' # HTTP or DNS
gsIPRange = '192.168.0.0/24' # "/24" means "first 24 bits are constant"
gsUIChoice = 'stdout' # one or more of: notification syslog stdout
# file of machines seen on the LAN; read and written by this application
gsDatabaseFilename = 'lanwatch.csv'
# used to identify vendors where official MAC lookup fails
# used to identify vendors where official MAC lookup fails; read by this application
gsMACVendorsFilename = 'lanwatch-MACVendors.csv'
gnPollingIntervalSeconds = 300
#--------------------------------------------------------------------------------------------------
@ -52,8 +53,8 @@ import sys
import platform
import time # https://www.cyberciti.biz/faq/howto-get-current-date-time-in-python/
import requests
import ipaddress
import os # https://docs.python.org/3/library/os.html
#import ipaddress
#import os # https://docs.python.org/3/library/os.html
import socket
import scapy.all as scapy
import csv # https://docs.python.org/3/library/csv.html
@ -89,10 +90,14 @@ if gbOSWindows:
# state variables
garrDevices = [] # each row = [MAC address, vendor, name, description]
garrDevices = [] # each row = [MAC address, vendor name, host name, description]
garrMACVendors = [] # each row = [MAC OIU, vendor name]
gsMyMACAddress = None # MAC address of this system
gsMyIPAddress = None # LAN IP address of this system
#--------------------------------------------------------------------------------------------------
@ -101,6 +106,8 @@ garrMACVendors = [] # each row = [MAC OIU, vendor name]
def DoARPScan():
global gsIPRange
global gsMyMACAddress
global gsMyIPAddress
arp_req = scapy.ARP(pdst=gsIPRange) # get an arp request
broadcast = scapy.Ether(dst="ff:ff:ff:ff:ff:ff") # Set the destination mac address
@ -111,10 +118,18 @@ def DoARPScan():
arrAddress = []
for element in answered:
# print('element '+str(element))
#print('element '+str(element))
# [MAC address, LAN IP address]
arrAddress.append([element[1].hwsrc, element[1].psrc])
if not gsMyMACAddress:
gsMyMACAddress = element[1].dst
gsMyIPAddress = element[1].pdst
# ARP scan doesn't get local system (machine this application is running on),
# so add it
if gsMyMACAddress:
arrAddress.append([gsMyMACAddress, gsMyIPAddress])
return arrAddress
@ -123,7 +138,7 @@ def DoARPScan():
# First 3 bytes (or 24 bits) of MAC address is the Organizationally Unique Identifier (OUI)
# and usually encodes the manufacturer.
def get_vendor(mac_address):
def GetVendorName(mac_address):
# free and public for up to 1000 requests/day
r = requests.get("https://api.macvendors.com/" + mac_address)
@ -139,7 +154,7 @@ def get_vendor(mac_address):
#--------------------------------------------------------------------------------------------------
def get_devicename(ip_address):
def GetDeviceName(ip_address):
# https://www.comparitech.com/net-admin/scan-for-ip-addresses-local-network/
# https://www.comparitech.com/net-admin/dhcp/
@ -156,11 +171,13 @@ def get_devicename(ip_address):
# gives mfr's domain or "_gateway" for router, and local IP addr for all others
sHostname = socket.getfqdn(ip_address)
print('get_devicename: ip_address '+ip_address+' gives hostname '+sHostname)
#print('GetDeviceName: ip_address '+ip_address+' gives hostname '+sHostname)
# works for some Windows 10 machines, have to be running NETBIOS ?
# doesn't work if this machine is running VPN ?
# nmblookup -A 192.168.0.11
# smbclient -L //192.168.0.11/printer
# findsmb
# https://github.com/samba-team/samba/blob/master/source3/utils/nmblookup.c
# works only if there is a DNS for the LAN (unlikely)
@ -242,11 +259,11 @@ def ReadVendors():
garrMACVendors = []
print('ReadVendors: called')
#print('ReadVendors: called')
objFile = open(gsMACVendorsFilename, "r", newline='')
objReader = csv.reader(objFile)
for row in objReader:
print('ReadVendors: got row '+str(row))
#print('ReadVendors: got row '+str(row))
garrMACVendors.append(row)
objReader = None
objFile.close()
@ -258,7 +275,7 @@ def CreateDatabase():
global gsDatabaseFilename
print('CreateDatabase: called')
#print('CreateDatabase: called')
f = open(gsDatabaseFilename,"w+")
f.close()
@ -272,11 +289,11 @@ def ReadDatabase():
garrDevices = []
print('ReadDatabase: called')
#print('ReadDatabase: called')
objDatabaseFile = open(gsDatabaseFilename, "r", newline='')
objDatabaseReader = csv.reader(objDatabaseFile)
for row in objDatabaseReader:
print('ReadDatabase: got row '+str(row))
#print('ReadDatabase: got row '+str(row))
garrDevices.append(row)
objDatabaseReader = None
objDatabaseFile.close()
@ -289,11 +306,11 @@ def WriteDatabase():
global gsDatabaseFilename
global garrDevices
print('WriteDatabase: called')
#print('WriteDatabase: called')
objDatabaseFile = open(gsDatabaseFilename, "w", newline='')
objDatabaseWriter = csv.writer(objDatabaseFile)
for row in garrDevices:
print('WriteDatabase: write row '+str(row))
#print('WriteDatabase: write row '+str(row))
objDatabaseWriter.writerow(row)
#objDatabaseWriter.writerow([time.strftime("%H:%M:%S")] + ['78901'])
#objDatabaseWriter.writerow([time.strftime("%H:%M:%S")] + ['jklmn'])
@ -307,7 +324,7 @@ def bIsMACAddressInDatabase(sMACAddress):
global garrDevices
print('IsMACAddressInDatabase: called, sMACAddress '+sMACAddress)
#print('IsMACAddressInDatabase: called, sMACAddress '+sMACAddress)
for row in garrDevices:
if (row[0] == sMACAddress):
return True
@ -327,7 +344,7 @@ if __name__ == '__main__':
try:
ReadDatabase()
except:
print('read "'+gsDatabaseFilename+'" failed, creating file')
#print('read "'+gsDatabaseFilename+'" failed, creating file')
try:
CreateDatabase()
except:
@ -337,17 +354,28 @@ if __name__ == '__main__':
while True:
arrAddress = DoARPScan()
print('arrAddress '+str(arrAddress))
#print('arrAddress '+str(arrAddress))
for arrDevice in arrAddress:
sMACAddress = arrDevice[0]
if (not bIsMACAddressInDatabase(sMACAddress)):
sVendor = get_vendor(sMACAddress)
sVendor = GetVendorName(sMACAddress)
sIPAddress = arrDevice[1]
sDeviceName = get_devicename(sIPAddress)
print('new sMACAddress '+sMACAddress+' == vendor "'+sVendor+'", name "'+sDeviceName+'"')
ReportNewDevice('New device on LAN: sMACAddress '+sMACAddress+' == vendor "'+sVendor+'", name "'+sDeviceName+'"')
garrDevices.append([sMACAddress, sVendor, sDeviceName, 'description'])
sDeviceName = GetDeviceName(sIPAddress)
sDescription = 'description'
#print('new sMACAddress '+sMACAddress+' == vendor "'+sVendor+'", name "'+sDeviceName+'"')
if sDeviceName == '_gateway':
sDescription = "router"
if sIPAddress == gsMyIPAddress:
sDeviceName = socket.gethostname()
ReportNewDevice('New device on LAN: '+sMACAddress+' == vendor "'+sVendor+'", name "'+sDeviceName+'"')
# read latest database file again in case someone edited it since last time we read it
try:
ReadDatabase()
except:
print('read "'+gsDatabaseFilename+'" failed')
sys.exit()
garrDevices.append([sMACAddress, sVendor, sDeviceName, sDescription])
try:
WriteDatabase()
except:
@ -356,7 +384,7 @@ if __name__ == '__main__':
time.sleep(1)
try:
time.sleep(15)
time.sleep(gnPollingIntervalSeconds)
except KeyboardInterrupt:
sys.exit()