MAC Address – How to Check the MAC Address of TCP-Connected Host on Same LAN

linux-networkingmac addressnetworkingpython

I have an Ethernet LAN (192.168.1.0/24) with a number of hosts, which are all assigned addresses over DHCP by a router. Due to the way that DHCP is set up the IP addresses of particular devices can change in unpredictable ways; this is something I have no control over.

There is some code running on a client host, which regularly polls server processes running on each of the other hosts, over TCP, to collect some metrics from each host. The client needs to be able to reliably identify which of the servers it's talking to – and due to the dynamic IP assignment, the only reliable identification is based on their MAC addresses.

I have taken the usual steps to obtain a MAC->IP mapping for each of the hosts:

  1. I periodically run a network scan using nmap, to make sure the ARP cache is populated
  2. Before establishing each connection to a server, I look up the MAC->IP mapping in the client's ARP cache (via a method like this). If the MAC isn't present in the ARP cache, I force a network re-scan.

This works most of the time, but I still encounter occasional issues due to the switching of IP addresses by hosts.

For example, for a while Host A will have IP address 192.168.1.101, and Host B will have IP address 192.168.1.102. Based on the ARP cache and knowledge of their MAC addresses, I can tell which is which and poll accordingly. But occasionally there will be an IP reassignment as DHCP leases are refreshed, and Host A will end up on 192.168.1.102, with Host B now on 192.168.1.101. At the moment that that happens, the client's ARP cache will be incorrect. And based on the above approach, the client will still connect to 192.168.1.101 thinking that it's reading Host A's metrics, while it's actually now talking to Host B.

I need to eliminate such cases.

The following three potential approaches have occurred to me:

  • Option A: Just before connecting to a host, have the client ping the IP it thinks it's on, in order to force an ARP refresh. Use that to check that it's about to connect to the right host/MAC.

  • Option B: After the TCP connection and readout, have the client check the ARP table again, in order to make sure the it actually connected to the MAC address it thought it was connected to. (from what I've seen, the ARP cache should have been updated by virtue of connecting)

  • Option C: When carrying out the TCP connection itself, check the remote MAC that the client is connecting to

I appreciate that this was a long-winded intro, but I'd basically like to know whether there is a practical way to implement Option C?

If I inspect the data packets being exchanged between the hosts during a TCP connection, the respective MAC addresses are present as the source and destination of the Ethernet frame. So in theory – at least on some level – the client device does know the specific MAC of the server it's talking to. But is there a way to expose this to the code that's initiating the TCP connection?

In my context I have Python code creating connections using the built-in socket library, on a Linux host. I appreciate that the answer may be heavily dependent on the actual environment – but I'd be interested to hear any general guidance on how MAC addresses are (or are not) exposed to processes that initiate TCP connections. And indeed any guidance on a reliable way to connect to a host based on its MAC address.

Best Answer

Socket APIs at layer 3 and 4 don't see the layer 2 MAC address. That requires investigation of the neighbor discovery cache. (And that your network stack involves MAC addresses at all. Although Linux boxes on a LAN probably will, Ethernet is ubiquitous.)

ARP also isn't a thing for IPv6 networks. You also should look at the neighbor discovery cache. A different thing, even if the interfaces to query it are similar.

And MAC addresses make poor primary keys in a table of hosts, they aren't the most stable or unique identifiers. Hosts have multiple NICs. Hardware NICs get replaced. When cloning a VM, guest NICs should be re-numbered. Lack of duplicates is likely but not certain.


Rather, use a long host-specific identifier. Such as a UUID, which is extremely likely to be unique. For example, systemd systems have /etc/machine-id.