From 39bbe94c230566e6226e8b09d4b39bd09b137ad7 Mon Sep 17 00:00:00 2001 From: Amar Saljic Date: Mon, 7 Jul 2025 17:51:10 +0200 Subject: [PATCH] created alert script for mikrotik which sends a push notification in case new devices log into the network --- mikrotik/arp-monitor.rsc | 145 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 mikrotik/arp-monitor.rsc diff --git a/mikrotik/arp-monitor.rsc b/mikrotik/arp-monitor.rsc new file mode 100644 index 0000000..a569d86 --- /dev/null +++ b/mikrotik/arp-monitor.rsc @@ -0,0 +1,145 @@ +# ARP Table Monitor Script for MikroTik RouterOS +# This script monitors ARP table and sends REST API notifications for new MAC+IP combinations + +# Configuration variables +:local apiEndpoint "http://10.10.10.6:8500/mikrotik-389210-instant" +:local monitorInterfaces {"vlan-guest"; "vlan-iot"; "vlan-home"} + +# Create global variable to store known MAC+IP combinations if it doesn't exist +:global arpKnownDevices +:if ([:typeof $arpKnownDevices] = "nothing") do={ + :set arpKnownDevices [:toarray ""] +} +# Uncomment while testing, comment for production +# :set arpKnownDevices [:toarray ""] + +# Get current ARP entries +:local currentArpEntries [:toarray ""] +:local arpEntries + +# Get ARP entries - filter by interface if specified +:if ([:len $monitorInterfaces] > 0) do={ + :set arpEntries [:toarray ""] + :foreach iface in=$monitorInterfaces do={ + :do { + :local ifaceEntries [/ip arp find interface=$iface] + :foreach entry in=$ifaceEntries do={ + :if ([:len $arpEntries] = 0) do={ + :set arpEntries $entry + } else={ + :set arpEntries ($arpEntries, $entry) + } + } + } on-error={ + :log warning "Could not access interface: $iface" + } + } + :if ([:len $arpEntries] > 0) do={ + :set arpEntries [:toarray $arpEntries] + } +} else={ + # Monitor all interfaces + :set arpEntries [/ip arp find] +} + +# Check for new MAC+IP combinations +:local newDevicesFound false +:local requestBody "" + +:if ([:len $arpEntries] > 0) do={ + :foreach arpEntry in=$arpEntries do={ + :local arpInfo [/ip arp get $arpEntry] + + # Skip incomplete, invalid, or dynamic entries if desired + :local arpStatus ($arpInfo->"status") + :if ($arpStatus = "reachable" || $arpStatus = "stale" || $arpStatus = "delay") do={ + + :local macAddress ($arpInfo->"mac-address") + :local ipAddress ($arpInfo->"address") + :local interface ($arpInfo->"interface") + + # Create unique identifier for MAC+IP combination + :local deviceId ($macAddress . "-" . $ipAddress) + + # Check if this MAC+IP combination is already known + :local isKnown false + :if ([:len $arpKnownDevices] > 0) do={ + :if ([:find $arpKnownDevices $deviceId] >= 0) do={ + :set isKnown true + } + } + + # If not known, process as new device + :if (!$isKnown) do={ + :set newDevicesFound true + + # Get additional info + :local arpComment "" + :if ([:typeof ($arpInfo->"comment")] != "nothing") do={ + :set arpComment ($arpInfo->"comment") + } else={ + :set arpComment "N/A" + } + + # Try to get hostname from DHCP lease table (if available) + :local hostname "Unknown" + :do { + :local dhcpLease [/ip dhcp-server lease find address=$ipAddress] + :if ([:len $dhcpLease] > 0) do={ + :local leaseInfo [/ip dhcp-server lease get [:pick $dhcpLease 0]] + :if ([:typeof ($leaseInfo->"host-name")] != "nothing") do={ + :set hostname ($leaseInfo->"host-name") + } + } + } on-error={ + # DHCP lookup failed, keep hostname as "Unknown" + } + + # Build notification body + :set requestBody ($requestBody . "MAC+IP Address: " . $deviceId . "\n") + :set requestBody ($requestBody . "Interface: " . $interface . "\n") + :set requestBody ($requestBody . "ARP Status: " . $arpStatus . "\n") + :set requestBody ($requestBody . "Hostname: " . $hostname . "\n") + :set requestBody ($requestBody . "----------------------------------------\n") + + # Add to known devices + :if ([:len $arpKnownDevices] = 0) do={ + :set arpKnownDevices $deviceId + } else={ + :set arpKnownDevices ($arpKnownDevices . "," . $deviceId) + } + + :log info "New ARP entry detected: $macAddress at $ipAddress on $interface" + } + } + } +} + +# Send REST API notification for new devices +:if ($newDevicesFound) do={ + :do { + /tool fetch \ + url=$apiEndpoint \ + http-method=post \ + http-header-field="Priority: 3,Title: New device detected in network,Tags: warning" \ + http-data=$requestBody \ + keep-result=no + + :log info "ARP monitoring notification API request sent" + } on-error={ + :log error "Failed to send ARP monitoring notification API request" + } +} else={ + :log info "No new mac/ip combinations in ARP" +} + +# Clean up known devices list if it gets too large +:local knownCount 0 +:if ([:len $arpKnownDevices] > 0) do={ + :set knownCount [:len [:toarray $arpKnownDevices]] +} +:if ($knownCount > 2000) do={ + :log warning "Known ARP devices list is getting large ($knownCount entries), consider resetting it" + # Uncomment the next line to auto-reset when list gets too large + # :global arpKnownDevices "" +} \ No newline at end of file