diff --git a/front/plugins/README.md b/front/plugins/README.md
index ee13e0643..f3852f37d 100755
--- a/front/plugins/README.md
+++ b/front/plugins/README.md
@@ -58,6 +58,7 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T
| `VNDRPDT` | ⚙ | Vendor database update | | | Script | [vendor_update](/front/plugins/vendor_update/) |
| `WEBHOOK` | ▶️ | Webhook notifications | | | Script | [_publisher_webhook](/front/plugins/_publisher_webhook/) |
| `WEBMON` | ♻ | Website down monitoring | | | Script | [website_monitor](/front/plugins/website_monitor/) |
+| `IPNEIGH` | 🔍 | Scan ARP (IPv4) and NDP (IPv6) tables | | | Script | [ipneigh](/front/plugins/ipneigh/) |
> \* The database cleanup plugin (`DBCLNP`) is not _required_ but the app will become unusable after a while if not executed.
diff --git a/front/plugins/ipneigh/README.md b/front/plugins/ipneigh/README.md
new file mode 100755
index 000000000..715ccce94
--- /dev/null
+++ b/front/plugins/ipneigh/README.md
@@ -0,0 +1,22 @@
+## Overview
+
+This plugin reads from the ARP and NDP tables using the `ip neigh` command.
+
+This differs from the `ARPSCAN` plugin because
+* It does *not* send arp requests, it just reads the table
+* It supports IPv6
+* It sends an IPv6 multicast ping to solicit IPv6 neighbour discovery
+
+### Quick setup guide
+
+To set up the plugin correctly, make sure to add in the plugin settings the name of the interfaces you want to scan. This plugin doesn't use the global `SCAN_SUBNET` setting, this is because by design it is not aware of subnets, it just looks at all the IPs reachable from an interface.
+
+### Usage
+
+- Head to **Settings** > **IP Neigh** to add the interfaces you want to scan to the `IPNEIGH_interfaces` option
+- The interface list must be formatted without whitespaces and comma separated e.g. `eth0,wl1,tap0`
+
+### Notes
+
+- `ARPSCAN` does a better job at discovering IPv4 devices because it explicitly sends arp requests
+- IPv6 devices will often have multiple addresses, but the ping answer will contain only one. This means that in general this plugin will not discover every address but only those who answer
\ No newline at end of file
diff --git a/front/plugins/ipneigh/config.json b/front/plugins/ipneigh/config.json
new file mode 100755
index 000000000..93c2059e3
--- /dev/null
+++ b/front/plugins/ipneigh/config.json
@@ -0,0 +1,434 @@
+{
+ "code_name": "ipneigh",
+ "unique_prefix": "IPNEIGH",
+ "plugin_type": "device_scanner",
+ "execution_order": "Layer_0",
+ "enabled": true,
+ "data_source": "script",
+ "mapped_to_table": "CurrentScan",
+ "data_filters": [
+ {
+ "compare_column": "Object_PrimaryID",
+ "compare_operator": "==",
+ "compare_field_id": "txtMacFilter",
+ "compare_js_template": "'{value}'.toString()",
+ "compare_use_quotes": true
+ }
+ ],
+ "show_ui": true,
+ "localized": [
+ "display_name",
+ "description",
+ "icon"
+ ],
+ "display_name": [
+ {
+ "language_code": "en_us",
+ "string": "IP Neigh"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Plugin to scan the ip tables"
+ }
+ ],
+ "icon": [
+ {
+ "language_code": "en_us",
+ "string": ""
+ }
+ ],
+ "params": [],
+ "settings": [
+ {
+ "function": "RUN",
+ "events": [
+ "run"
+ ],
+ "type": {
+ "dataType": "string",
+ "elements": [
+ {
+ "elementType": "select",
+ "elementOptions": [],
+ "transformers": []
+ }
+ ]
+ },
+ "default_value": "disabled",
+ "options": [
+ "disabled",
+ "once",
+ "schedule",
+ "always_after_scan",
+ "on_new_device",
+ "on_notification"
+ ],
+ "localized": [
+ "name",
+ "description"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "When to run"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "When the plugin should run. Good options are always_after_scan
, on_new_device
, on_notification
"
+ }
+ ]
+ },
+ {
+ "function": "RUN_SCHD",
+ "type": {
+ "dataType": "string",
+ "elements": [
+ {
+ "elementType": "input",
+ "elementOptions": [],
+ "transformers": []
+ }
+ ]
+ },
+ "default_value": "*/5 * * * *",
+ "options": [],
+ "localized": [
+ "name",
+ "description"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Schedule"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Only enabled if you select schedule
in the SYNC_RUN
setting. Make sure you enter the schedule in the correct cron-like format (e.g. validate at crontab.guru). For example entering 0 4 * * *
will run the scan after 4 am in the TIMEZONE
you set above. Will be run NEXT time the time passes."
+ }
+ ]
+ },
+ {
+ "function": "interfaces",
+ "type": {
+ "dataType": "string",
+ "elements": [
+ {
+ "elementType": "input",
+ "elementOptions": [],
+ "transformers": []
+ }
+ ]
+ },
+ "maxLength": 150,
+ "default_value": "eth0",
+ "options": [],
+ "localized": [
+ "name",
+ "description"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Interfaces to scan"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "The plugin will scan these comma separated interfaces"
+ }
+ ]
+ },
+ {
+ "function": "CMD",
+ "type": {
+ "dataType": "string",
+ "elements": [
+ {
+ "elementType": "input",
+ "elementOptions": [
+ {
+ "readonly": "true"
+ }
+ ],
+ "transformers": []
+ }
+ ]
+ },
+ "default_value": "python3 /app/front/plugins/ipneigh/ipneigh.py ipneigh_interfaces={IPNEIGH_interfaces}",
+ "options": [],
+ "localized": [
+ "name",
+ "description"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Command"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Command to run. This can not be changed"
+ }
+ ]
+ },
+ {
+ "function": "RUN_TIMEOUT",
+ "type": {
+ "dataType": "integer",
+ "elements": [
+ {
+ "elementType": "input",
+ "elementOptions": [
+ {
+ "type": "number"
+ }
+ ],
+ "transformers": []
+ }
+ ]
+ },
+ "default_value": 30,
+ "options": [],
+ "localized": [
+ "name",
+ "description"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Run timeout"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Maximum time in seconds to wait for the script to finish. If this time is exceeded the script is aborted."
+ }
+ ]
+ }
+ ],
+ "database_column_definitions": [
+ {
+ "column": "Index",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "none",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Index"
+ }
+ ]
+ },
+ {
+ "column": "Object_PrimaryID",
+ "mapped_to_column": "cur_MAC",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "device_name_mac",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "MAC"
+ }
+ ]
+ },
+ {
+ "column": "Object_SecondaryID",
+ "mapped_to_column": "cur_IP",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "device_ip",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "IP"
+ }
+ ]
+ },
+ {
+ "column": "Watched_Value1",
+ "mapped_to_column": "cur_Name",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Name"
+ }
+ ]
+ },
+ {
+ "column": "Watched_Value2",
+ "mapped_to_column": "cur_Vendor",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Vendor"
+ }
+ ]
+ },
+ {
+ "column": "Watched_Value3",
+ "mapped_to_column": "cur_Type",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Device Type"
+ }
+ ]
+ },
+ {
+ "column": "Watched_Value4",
+ "css_classes": "col-sm-2",
+ "show": false,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "N/A"
+ }
+ ]
+ },
+ {
+ "column": "Dummy",
+ "mapped_to_column": "cur_ScanMethod",
+ "mapped_to_column_data": {
+ "value": "ip neighbor"
+ },
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Scan method"
+ }
+ ]
+ },
+ {
+ "column": "DateTimeCreated",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Created"
+ }
+ ]
+ },
+ {
+ "column": "DateTimeChanged",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Changed"
+ }
+ ]
+ },
+ {
+ "column": "Status",
+ "css_classes": "col-sm-1",
+ "show": true,
+ "type": "replace",
+ "default_value": "",
+ "options": [
+ {
+ "equals": "watched-not-changed",
+ "replacement": "