Class MacResolver

java.lang.Object
org.openhab.core.io.net.mac.MacResolver

@NonNullByDefault public class MacResolver extends Object
Utility class for resolving MAC addresses from IPv4 addresses via the operating system's ARP cache. The main method resolveMac(String) provides an asynchronous API to get the MAC address for a given IPv4 address. If the MAC address is cached and valid, it returns immediately. Otherwise, it starts a front end process that involves probing the IPv4 address to trigger the OS to populate its ARP table, plus a back end process that bulk loads the OS ARP cache into the in-memory cache and completes the pending future when the MAC address becomes available. The implementation includes optimizations to avoid unnecessary ARP cache loads for non-local or unreachable IP addresses, and to share pending resolution tasks for the same IP address to avoid redundant work. Resolved MAC addresses are cached in-memory with an expiration time to avoid frequent lookups, and the back end process is scheduled to run only when there are pending resolutions to avoid unnecessary resource usage. This class is designed to be thread-safe and efficient for typical home network environments where devices may come and go, and ARP cache entries may expire or change over time.
Author:
Andrew Fiddian-Green - Initial contribution
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    protected static class 
    Simple wrapper class to hold a MAC address with its expiration time-stamp.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
     
  • Constructor Summary

    Constructors
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    protected void
    Periodic task that is run by a back end scheduler that loads the ARP cache from the operating system.
    protected static boolean
    Checks if the text begins with a standard format and valid IP address. e.g.
    protected @Nullable String
    Retrieves a MAC address from the in-memory cache, if present and not expired.
    protected void
    Stores an IP => MAC mapping in the cache with expiration and if possible eagerly resolves any pending MAC future(s).
    protected void
     
    protected boolean
    Checks if the given IPv4 address is on the same local sub-net as any of the host's network interfaces.
    protected static boolean
    Checks whether the given MAC address matches typical OS-supported formats, such as AA:BB:CC:DD:EE:FF, AA-BB-CC-DD-EE-FF, or aa-bb-cc-dd-ee-ff.
    protected static String
    Extracts IP part of a string. e.g. both 192.168.1.1:8080 and 192.168.1.1 produce the output 192.168.1.1
    protected static String
    Converts a MAC address to the standard format XX:XX:XX:XX:XX:XX.
    protected void
    Parses a single line from ARP output, extracts the IP MAC mapping if present, and caches it.
    resolveMac(String ipv4Address)
    Resolves the MAC address for a given IPv4 address.
    protected void
    Triggers the operating system to update its ARP table for the given IP address by performing probes on all valid network interfaces.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

  • Constructor Details

    • MacResolver

      public MacResolver()
  • Method Details

    • deactivate

      protected void deactivate()
    • resolveMac

      public CompletableFuture<@Nullable String> resolveMac(String ipv4Address)
      Resolves the MAC address for a given IPv4 address. If the MAC address is cached and valid, it is returned immediately. Otherwise, an asynchronous resolution process is started that involves a front end process that probes the IPv4 target device to trigger the OS to populate its ARP table, and a back end process that bulk loads the ARP table and completes the future when the MAC address becomes available. The future completes with null if resolution fails or takes too long. The method also includes optimizations to avoid unnecessary ARP cache loads for invalid, non-local, or unreachable IPv4 addresses by checking these conditions before scheduling the asynchronous resolution.
      Parameters:
      ipv4Address - the IPv4 address to resolve e.g. "192.168.1.1" or "192.168.1.1:port"
      Returns:
      a future that completes with the resolved MAC address or null if resolution fails or times out
    • triggerArpTableUpdate

      protected void triggerArpTableUpdate(String ipv4)
      Triggers the operating system to update its ARP table for the given IP address by performing probes on all valid network interfaces. The method sends a single UDP data-gram to the discard port 9 to do the probe. However before the OS can actually try to send the data-gram, it first has to check if it has the target MAC address. And if not it must send an ARP request packet to resolve the MAC. In other words, by doing the probe we immediately trigger the ARP resolution process, and for our purposes it does not matter how or if the target device responds. Each probe is scoped to the candidate interfaces that are up, non-loopback, and on the same sub-net as the target IP.
      Parameters:
      ipv4 - the target IPv4 address to trigger ARP resolution for
    • isOnLocalSubnet

      protected boolean isOnLocalSubnet(InetAddress address)
      Checks if the given IPv4 address is on the same local sub-net as any of the host's network interfaces. This avoids ARP cache loads for IPv4 addresses that are not local which therefore cannot be resolved to a MAC address via the OS ARP table.
    • backEndTask

      protected void backEndTask()
      Periodic task that is run by a back end scheduler that loads the ARP cache from the operating system. If there are no more pending futures, the back end scheduler is stopped to avoid unnecessary resource usage.
    • cacheGet

      protected @Nullable String cacheGet(String ip)
      Retrieves a MAC address from the in-memory cache, if present and not expired.
    • cachePut

      protected void cachePut(String ip, String mac)
      Stores an IP => MAC mapping in the cache with expiration and if possible eagerly resolves any pending MAC future(s).
    • isValidMac

      protected static boolean isValidMac(String mac)
      Checks whether the given MAC address matches typical OS-supported formats, such as AA:BB:CC:DD:EE:FF, AA-BB-CC-DD-EE-FF, or aa-bb-cc-dd-ee-ff. Mixed separators are also accepted. The method additionally rejects the all-zero MAC 00:00:00:00:00:00.
    • normalizeMac

      protected static String normalizeMac(String mac)
      Converts a MAC address to the standard format XX:XX:XX:XX:XX:XX.
    • beginsWithValidIp

      protected static boolean beginsWithValidIp(String ip)
      Checks if the text begins with a standard format and valid IP address. e.g. 192.168.1.1 and 192.168.1.1:1234 are valid whereas 999.999.999.999 or foo 192.168.1.1 are not.
      Parameters:
      ip - the IP address to check
      Returns:
      true if the text begins with a valid IP address, false otherwise
    • normalizeIp

      protected static String normalizeIp(String ip)
      Extracts IP part of a string. e.g. both 192.168.1.1:8080 and 192.168.1.1 produce the output 192.168.1.1
      Parameters:
      ip - the IP address to normalize
      Returns:
      the normalized IP address, or the original string if it cannot be normalized
    • parseLine

      protected void parseLine(String line)
      Parses a single line from ARP output, extracts the IP MAC mapping if present, and caches it.
      Parameters:
      line - the line to parse