Sunday, August 28, 2016

Get IP address by MAC address in C#. Updating ARP table.

Recently I have written article about getting IP address of connected to the network device using it MAC address, you can find it using the following link Get IP address by MAC address in C#. It seemed to me that the solution was good until I figured out that it requires some time for ARP table being updated.
To reproduce such situation you just need to reboot your computer or execute command:
arp -d

Run cmd.exe as administrator, type command text and hit enter. Now the code provided in the previous article does not work as expected.

Fixing the issue


The device should be on the same sub-network as running computer. So we need to ping all the IP addresses int the sub-network, e.g. (192.168.1.1 - 192.168.1.255) to update the ARP table. I have updated IpFinder class with necessary code.

IpFinder.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace SftpDownloader.Core
{
    public class IpFinder
    {
        public string FindIpAddressByMacAddress(string macAddress, string currentIpAddress)
        {
            Parallel.ForEach(GetListOfSubnetIps(currentIpAddress), delegate (string s)
            {
                DeviceScanner.IsHostAccessible(s);
            });

            var arpEntities = new ArpHelper().GetArpResult();
            var ip = arpEntities.FirstOrDefault(
                a => string.Equals(
                    a.MacAddress,
                    macAddress,
                    StringComparison.CurrentCultureIgnoreCase))?.Ip;

            return ip;
        }

        private List<string> GetListOfSubnetIps(string currentIp)
        {
            var a = currentIp.Split('.');
            var lst = new List<string>();

            for (int i = 1; i <= 255; i++)
            {
                lst.Add($"{a[0]}.{a[1]}.{a[2]}.{i}");
            }

            lst.Remove(currentIp);

            return lst;
        }
    }
}

DeviceScanner.cs


using System.Net.NetworkInformation;

namespace SftpDownloader.Core
{
    public class DeviceScanner
    {
        public static bool IsHostAccessible(string hostNameOrAddress)
        {
            Ping ping = new Ping();
            PingReply reply = ping.Send(hostNameOrAddress, 1000);

            return reply != null && reply.Status == IPStatus.Success;
        }
    }
}