# 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 "" }