Metadata-Version: 2.4
Name: pfarpwatch
Version: 1.0
Summary: Watch for new LAN clients on a pfSense managed network
Author-email: Chris Nelson <github@cjnaz.com>
License-Expression: MIT
Project-URL: repository, https://github.com/cjnaz/pfarpwatch
Keywords: cjnaz,lan,router,pfsense,arp,monitor,arpwatch
Classifier: Topic :: System :: Networking :: Monitoring
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cjnfuncs>=3.2.1
Requires-Dist: requests
Dynamic: license-file

# pfarpwatch - Watch for new clients on your pfSense managed LAN


I use pfSense as my home network router and find that over time that I've accumulated many clients on my network that I cannot readily identify. 
pfarpwatch watches the pfSense ARP table and tracks changes in a sqlite3 database.
Any new client found on the network results in a text message notification.

- The history of known clients is tracked by MAC address. No changes are ever made on the pfSense device.
- Clients come and go over time, as family members come and visit.
- Clients may be manually deleted from the database (i.e., *That laptop went in the tub with kids!* (long gone)).
- Some hostnames are ambiguous, such as '*' and 'android-2ab8700dff69dbfd', and some devices issue random MAC addresses. Notes may be manually added for each client. 
- The Organization Unique ID for for each clients' MAC address is looked up and added to the database, often providing enough info to identify strange clients.
- The clients database may easily be queried, with sort order and search/filter options.

Definition of terms:
- _client_ is used in this documentation to refer to a host/client on your network.
- _device_ refers to a pfSense(+) router on your network.

Supports
- Linux and Windows
- Python 3.9+
- pfSense+ 25.07.1 and above, and corresponding pfSense CE versions (2.8.0+?)
  - Tested on 25.07.1, 25.11 and 26.03, 26.07
- pfSense+ MIM API and UnofficialV2 API modes


## _But why this tool, versus other solutions?_

The key benefits of pfarpwatch over others are that
1) Through the pfSense router, pfarpwatch has visibility over all subnets/vlans (and across multiple pfSense+ devices in a MIM configuration), and 
2) The collected client data can be annotated (and searched and sorted).  

### Compared to arpwatch on pfSense

Do use the arpwatch package on pfSense as your best alternative.  The downsides of this solution is that 1) You most log into the pfSense router to view the data (but you'll have a nice GUI), and 2) logged client info cannot be annotated, so you'll have a collection of unidentifiable clients in the log.

### arpwatch on a network client machine

Running the standard arpwatch tool (published by LBNL and available in many Linux distro repos) on a client on your network will log/track clients _only on your client's subnet, not the whole LAN network_.  Also no annotation capability.

<br/>

---

## Notable changes since prior release (routermonitor V4.0.1)

- Changed from tracking the DHCP server to tracking the ARP table.
- Dropped the expiry field, and now last_seen is tracked for all clients **.
- Dropped page scrape mode.
- Customizable reporting with user settable field widths and order.
- With the CLI `--sort-by` switch the desired field may be specified by just the first unique characters; e.g., `--sort-by h` specifies to sort by hostname.
- Both email and notification options, with selectable client field changes
- Local database of IEEE macoui's is maintained, with user-scheduled update frequency.
- Renamed to pfarpwatch 1.0.

** Note:  ARP Table records show time to expiry of each arp record (e.g., "Expires in 1138 seconds"), and clients must re-announce themselves (or produce other traffic) before this timeout.  pfarpwatch calculates when a client was _last seen_ using the net.link.ether.inet.max_age (typically/default 1200 seconds / 20 minutes).  The `ARP_timeout` param in the config file must match the pfSense value (you may check it in the GUI:  Enter `sysctl net.link.ether.inet.max_age` on Diagnostics > Command Prompt > Execute Shell Command).


<br/>

---

## Usage
```
$ pfarpwatch --help
usage: pfarpwatch [-h] [--update] [--list-db] [--list-arp-table] [--sort-by SORT_BY] [--create-db] [--MAC MAC] [--note NOTE] [--delete] [--config-file CONFIG_FILE] [--print-log] [--service]
                  [--verbose] [--setup-user] [--setup-site] [--create-macoui-db] [--log-console] [--dev] [--version]
                  [SearchTerm]

Monitor clients on the network

Track pfSense's Diagnostics > ARP Table for recently seen network clients.
Any new clients are added to the tracking database, and a notification or email may be sent.
The database is easily searched, with customizable report formatting.

    $ pfarpwatch
        Prints the tracked clients list (defaults to --list-db)

    $ pfarpwatch --sort-by mac
        Prints the tracked clients list sorted by MAC address

    $ pfarpwatch amazon
        Prints the tracked clients list filtered for the search term in any field

    $ pfarpwatch --list-arp-table
        Prints the current pfSense ARP Table contents
1.0

positional arguments:
  SearchTerm            Print database records containing this text

options:
  -h, --help            show this help message and exit
  --update, -u          Check the pfSense router for new network clients and update the database
  --list-db, -l         Print known clients on the network from the database (default mode)
  --list-arp-table, -r  Print known clients on the network from the pfSense router ARP table
  --sort-by SORT_BY, -s SORT_BY
                        Sort --list-db and --list-arp-table output. Overrides config SortBy (default <hostname> if neither specified).
  --create-db           Create a fresh database and populate it with the current network clients
  --MAC MAC, -m MAC     MAC address for --note or --delete
  --note NOTE, -n NOTE  Add a note to the database for the specified --MAC
  --delete              Delete from the database the specified --MAC
  --config-file CONFIG_FILE, -c CONFIG_FILE
                        Path to the config file (Default <pfarpwatch.cfg)> in user/site config directory
  --print-log, -p       Print the tail end of the log file (default last 40 lines)
  --service             Run updates in an endless loop for use as a systemd service
  --verbose, -v         Print status and activity messages in non-service mode (-vv for debug logging)
  --setup-user          Install starter files in user space
  --setup-site          Install starter files in system-wide space - run with root prev
  --create-macoui-db    Build a fresh macoui database
  --log-console, -z     Force logging to the console, overriding the config LogFile param
  --dev                 Run development code
  --version, -V         Print version number and exit
```

<br/>

---

## Example output
```
$ pfarpwatch 
 WARNING:  ========== pfarpwatch (1.0) ==========
 WARNING:  Config file </path/to/pfarpwatch.cfg>
hostname                   first_seen           last_seen **         ip               device             mac                mac_oui                         notes
Denon-AVR-X1600H           2020-05-22 18:23:30  2025-11-29 21:29:18  192.168.1.112    pfsense.mylan      00:05:cd:8a:ab:8d  Denon, Ltd.                     -
Galaxy-S10-jen             2020-05-22 18:23:33  2025-11-30 11:01:02  192.168.1.114    pfsense.mylan      10:98:c3:80:cd:b2  Murata Manufacturing Co., Ltd.  -
amazon-b6f1c2033           2020-05-23 06:45:05  2025-11-30 11:06:36  192.168.1.118    pfsense.mylan      38:f7:3d:16:ef:40  Amazon Technologies Inc.        Wife's Kindle Fire
espressif                  2020-05-22 18:23:35  2025-11-30 11:23:37  192.168.2.121    pfsense.mylan      44:67:55:02:01:7f  Orbit Irrigation                -
Flex5                      2020-05-22 18:23:36  2025-11-30 11:25:41  192.168.1.123    pfsense.mylan      50:5b:c2:e1:23:ef  Liteon Technology Corporation   -
*                          2020-05-22 18:23:37  2025-11-30 11:31:48  192.168.1.144    pfsense.mylan      64:52:99:90:45:aa  The Chamberlain Group, Inc      Liftmaster gateway 828LM in office
MyQ-F8C                    2020-05-22 18:23:38  2025-11-30 11:32:15  192.168.1.143    pfsense.mylan      64:52:99:91:67:51  The Chamberlain Group, Inc      Garage door opener
ESP_48CEBF                 2020-05-22 18:23:40  2025-11-30 11:33:19  192.168.2.146    pfsense.mylan      80:7d:3a:48:89:bf  Espressif Inc.                  Basement lights smartswitch
*                          2020-05-22 18:23:41  2025-11-30 11:34:23  192.168.2.133    pfsense.mylan      8c:85:80:1d:ab:69  Smart Innovation LLC            Eufy doorbell
RPi1                       2020-05-22 18:23:42  2025-11-30 11:36:13  192.168.1.31     pfsense.mylan      b8:27:eb:25:cd:f7  Raspberry Pi Foundation         -
FireStick4k                2020-05-22 18:23:44  2025-11-30 11:37:04  192.168.1.40     pfsense.mylan      cc:9e:a2:56:ef:c9  Amazon Technologies Inc.        -
...
  <73>  known clients.
```

<br/>

---

## Setup and Usage notes
- Install and configure/enable the pfSense arpwatch package. (Tested on arpwatch version 0.2.5.)
  - Track all interfaces/subnets of interest on your LAN.
  - Disable bogons and 0.0.0.0 changes
- Install pfarpwatch from PyPI (`pip install pfarpwatch`)
- Install the initial configuration files (`pfarpwatch --setup-user` places files at `~/.config/pfarpwatch`).
- Decide on which DHCP clients list lookup method you wish to use (see more details below).  Both modes result in the same content in pfarpwatch:
  - `Mode = MIM_API` is the best choice if you are using a Netgate pfSense+ device or have a Plus license - _reasonably fast_. If using the MIM API you will need to manually install the Netgate pfsense-api (see below).
  - `Mode = Unofficial_APIV2` is a solid choice - _fastest_.
- Edit/configure `pfarpwatch.cfg`, `creds_SMTP`, and `creds_pfarpwatch` as needed.  The supplied `pfarpwatch.cfg` explains each setting and the defaults.
- Run `pfarpwatch` once manually to build the clients database and the MAC _Organizationally Unique Identifier_ database.
- Do `pfarpwatch --note` runs to annotate client info, as desired.  Example: `pfarpwatch --MAC 80:7d:3a:48:ce:bf --note "Basement lights smartswitch"`.
- `pfarpwatch --list-db` (equivalent to just `pfarpwatch`) provides a list of all known clients over time.  
  - `pfarpwatch --sort-by hostname` may be useful.  The report may be sorted by _mac, hostname, ip, device, first_seen, last_seen, notes, or mac_oui_.  The default `SortBy` may be set in the config file.  Sort terms may be shortened to the first unique characters, e.g., `-s m` to sort by MAC address.
  - `pfarpwatch amaz` filters for all clients in the database that have the string 'amaz' (case insensitive) in any field (two in the above example output) while `pfarpwatch .2.` lists all clients on my Guest WiFi (192.168.2.*, three in the above example output).  Filtering is supported with `--list-arp-table` also.
- `pfarpwatch --list-arp-table` provides a list of the currently known network clients. `--sort-by` is supported with fields _mac, hostname, ip, device, and last_seen_.  Filtering is also supported.
- `pfarpwatch --update` finds any new clients on the network, adds them to the database, and optionally sends a text and/or email message notification (see pfarpwatch.cfg). Changes to hostname, ip address, device, last_seen, and mac_oui may also be logged and have text/email notifications sent.
- Optionally set up the pfarpwatch systemd service. A template .service file is provided in the config directory.
  - When running in service mode (continuously looping) the config file may be edited and is reloaded when changed. This allows for changing settings without having to restart the service.

<br/>

---

## Using the Netgate Nexus MIM API (pfSense+ devices/appliances only) (`Mode = MIM_API`)

Setup
- Clone the Python interface github distribution to your local filesystem

			cd <my-temp-space>
			git clone https://github.com/Netgate/pfsense-api.git
			# creates ./pfsense-api
			pip install ./pfsense-api/py
			# Once installed the cloned directory <my-temp-space>/pfsense-api may be deleted.
	
- In the pfsense+ GUI set the device to HTTPS access mode
  - The API will not work in HTTP mode
  - System > Advance > Admin Access > Protocol = HTTPS (SSL/TLS) 
    - This uses a self-signed certificate, so your browser may want your approval to connect.
    - For more secure access, see below for setting up a certificate authority. Do this step before enabling Netgate Nexus Controller so that port 8443 is properly set up with the _internally signed 'Server Certificate'_.  If you set up the CA after enabling Netgate Nexus then simply disable and re-enable Netgate Nexus Controller again.

- Enable Netgate Nexus
	- System > Advanced > Netgate Nexus > Enable Netgate Nexus Controller
		- This enables the official Nexus MIM API.  Without paying for a license you can access only the `localhost` device.
		- See https://docs.netgate.com/pfsense/en/latest/nexus/setup.html
		- The user must have full admin privileges (be a member of the admins group), as of 25.07.1 RELEASE.
- **See the pfarpwatch.cfg starter file for configuring access to the MIM API.**

Notes and considerations

- This API returns the same information as on the Diagnostics > ARP Table page.  Note that this table only has a 20 minute retention window by default, so set the `UpdateInterval` param to less than 20 minutes.
- Netgate Nexus and the MIM API were first released on pfSense+ version 25.07. For older pfSense+ versions and the CE version see the Unofficial V2 API.
- Hostnames come from any DHCP server assigned hostnames, with fallback to whatever the client provides.  Windows clients may have a '.' appended to the hostname.
- Netgate Nexus on pfSense+ devices provides the multi-instance management (MIM) API, with specific support for accessing/controlling multiple pfSense+ devices on a network from a single "controller" (the master pfSense+ device).  MIM API accesses have a `device_id` field, which specifies which pfSense+ instance the API request targets.  `pfarpwatch` supports specifying a series of `Devices` in the config file.  The devices will be accessed in the order listed with all found clients merged into one clients list (unique by MAC address).  The default Devices lists is `['localhost']`. 'Devices = All' causes the MIM 
controller to be queried for the list of managed devices.  If you have a multi-instance network you will need a paid subscription to use Nexus across devices, and then the MIM API can also be used across devices.
Without a paid subscription the MIM API may be used for reading the ARP table from only the main 'localhost' MIM controller.

<br/>

---

## Using the Unofficial V2 API (`Mode = Unofficial_APIV2`)

Setup
- Install the Unofficial V2 API on your pfSense device.  This API works on both Netgate pfSense+ devices (24.11+) and on CE devices (2.8.0+).  See https://pfrest.org/INSTALL_AND_CONFIG/. The install can be done via an SSH login, using the device console, or using the GUI Diagnostics > Command Prompt > Execute Shell Command. Example for pfSense+ 26.03 (_do install the correct version_):

      pkg-static -C /dev/null add https://github.com/jaredhendrickson13/pfsense-api/releases/latest/download/pfSense-26.03-pkg-RESTAPI.pkg

- To enable the API, briefly, you will need to, at System > REST API > Settings, Enable the API, set Allowed Interfaces, and set up the Authentication Method to `Key`.  On the Keys tab, create a key and save the key value to the `creds_pfarpwatch` config file `API_key` param.


Notes and considerations
- This API returns the same information as on the Diagnostics > ARP Table page.  Note that this table only has a 20 minute retention window by default, so set the `UpdateInterval` param to less than 20 minutes.
- This API runs faster that the MIM API.
- Hostnames come from any DHCP server assigned hostnames, with fallback to whatever the client provides.  Windows clients may have a '.' appended to the hostname.
- The reported device field will always be the URL to the pfSense device since there is no multi-device support.
- After a pfSense upgrade you will need to install the new/correct version of the Unofficial V2 API.  Note that the previously issued key still seems to be in-place and active, so after installing the updated API all should be well.


<br/>

---
## Using a Certificate Authority

Each access mode supports verified SSL access by configuring a certificate authority within pfSense. 
In short, to set up certificates for use with pfarpwatch:

  - Create a _self-signed 'CA certificate'_ (System > Certificates > Authorities), then 'Export CA' to a file.  This is the CA public key.  Set the path to this file in the pfarpwatch config file `CA_path` param.  Note that the directory path to the CA file defaults to `~/.config/pfarpwatch` - an absolute path or a path relative to ~/.config/pfarpwatch may be specified.
  - Create a _internally signed 'Server Certificate'_ (System > Certificates > Certificates) that refers to the new CA certificate (thus _not_ a _self-signed server certificate_), with a Common Name (CN) or SAN entry set to the URL being used to access the device by pfarpwatch.
  - Change the webGUI (webConfigurator) to use the new internally signed server certificate (System > Advanced > Admin Access > SSL/TLS Certificate).
  Once set, the new server certificate will show as in use by the 'webConfigurator'. Delete the original server cert. Your browser may need some nudging at this point.
  - If applicable, change the MIM API to use the new internally signed server certificate (System > Advanced > Netgate Nexus > TLS Certificate).  You may need to disable/re-enable Netgate Nexus to propagate the new certificate setup to port 8443.

Notes
- Use of a CA is optional.  `CA_path` defaults to False if not defined, which disables SSL verification.
- When upgrading pfSense to a new version, these certificates seem to work correctly without needing any attention after the upgrade.


<br/>

---

## Version history
- 1.0 260828
  - Ported from routermonitor
  - Changed from tracking the DHCP server to tracking the ARP table
  - Dropped the expiry field, and now last_seen is tracked for all clients
  - Dropped page scrape mode
  - Customizable reporting with user settable field widths and order
  - With the CLI `--sort-by` switch the desired field may be specified by just the first unique characters
  - Text and email notifications supported for user selectable client field changes
  - Local database of IEEE macoui's is maintained, with user-scheduled update frequency.
