How do I access ARP-protocol information through .NET?

47,821

Solution 1

If you know which devices are out there you can use the Ping Class. This will allow you to at least fill up the ARP table. You can always execute ARP -a and parse the output if you have to. Here is also a link that shows how to pinvoke to call GetIpNetTable. I have included examples below of Ping Class and how to access the ARP table using the GetIpNetTable.

This is an example for the Ping Class

using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;

namespace Examples.System.Net.NetworkInformation.PingTest
{
    public class PingExample
    {
        // args[0] can be an IPaddress or host name.
        public static void Main (string[] args)
        {
            Ping pingSender = new Ping ();
            PingOptions options = new PingOptions ();

            // Use the default Ttl value which is 128,
            // but change the fragmentation behavior.
            options.DontFragment = true;

            // Create a buffer of 32 bytes of data to be transmitted.
            string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
            byte[] buffer = Encoding.ASCII.GetBytes (data);
            int timeout = 120;
            PingReply reply = pingSender.Send (args[0], timeout, buffer, options);
            if (reply.Status == IPStatus.Success)
            {
                Console.WriteLine ("Address: {0}", reply.Address.ToString ());
                Console.WriteLine ("RoundTrip time: {0}", reply.RoundtripTime);
                Console.WriteLine ("Time to live: {0}", reply.Options.Ttl);
                Console.WriteLine ("Don't fragment: {0}", reply.Options.DontFragment);
                Console.WriteLine ("Buffer size: {0}", reply.Buffer.Length);
            }
        }
    }
}

This is an example of the GetIpNetTable.

using System;
using System.Runtime.InteropServices;
using System.ComponentModel; 
using System.Net;

namespace GetIpNetTable
{
   class Program
   {
      // The max number of physical addresses.
      const int MAXLEN_PHYSADDR = 8;

      // Define the MIB_IPNETROW structure.
      [StructLayout(LayoutKind.Sequential)]
      struct MIB_IPNETROW
      {
         [MarshalAs(UnmanagedType.U4)]
         public int dwIndex;
         [MarshalAs(UnmanagedType.U4)]
         public int dwPhysAddrLen;
         [MarshalAs(UnmanagedType.U1)]
         public byte mac0;
         [MarshalAs(UnmanagedType.U1)]
         public byte mac1;
         [MarshalAs(UnmanagedType.U1)]
         public byte mac2;
         [MarshalAs(UnmanagedType.U1)]
         public byte mac3;
         [MarshalAs(UnmanagedType.U1)]
         public byte mac4;
         [MarshalAs(UnmanagedType.U1)]
         public byte mac5;
         [MarshalAs(UnmanagedType.U1)]
         public byte mac6;
         [MarshalAs(UnmanagedType.U1)]
         public byte mac7;
         [MarshalAs(UnmanagedType.U4)]
         public int dwAddr;
         [MarshalAs(UnmanagedType.U4)]
         public int dwType;
      }

      // Declare the GetIpNetTable function.
      [DllImport("IpHlpApi.dll")]
      [return: MarshalAs(UnmanagedType.U4)]
      static extern int GetIpNetTable(
         IntPtr pIpNetTable,
         [MarshalAs(UnmanagedType.U4)]
         ref int pdwSize,
         bool bOrder);

      [DllImport("IpHlpApi.dll", SetLastError = true, CharSet = CharSet.Auto)]
      internal static extern int FreeMibTable(IntPtr plpNetTable);

      // The insufficient buffer error.
      const int ERROR_INSUFFICIENT_BUFFER = 122;

      static void Main(string[] args)
      {
         // The number of bytes needed.
         int bytesNeeded = 0;

         // The result from the API call.
         int result = GetIpNetTable(IntPtr.Zero, ref bytesNeeded, false);

         // Call the function, expecting an insufficient buffer.
         if (result != ERROR_INSUFFICIENT_BUFFER)
         {
            // Throw an exception.
            throw new Win32Exception(result);
         }

         // Allocate the memory, do it in a try/finally block, to ensure
         // that it is released.
         IntPtr buffer = IntPtr.Zero;

         // Try/finally.
         try
         {
            // Allocate the memory.
            buffer = Marshal.AllocCoTaskMem(bytesNeeded);

            // Make the call again. If it did not succeed, then
            // raise an error.
            result = GetIpNetTable(buffer, ref bytesNeeded, false);

            // If the result is not 0 (no error), then throw an exception.
            if (result != 0)
            {
               // Throw an exception.
               throw new Win32Exception(result);
            }

            // Now we have the buffer, we have to marshal it. We can read
            // the first 4 bytes to get the length of the buffer.
            int entries = Marshal.ReadInt32(buffer);

            // Increment the memory pointer by the size of the int.
            IntPtr currentBuffer = new IntPtr(buffer.ToInt64() +
               Marshal.SizeOf(typeof(int)));

            // Allocate an array of entries.
            MIB_IPNETROW[] table = new MIB_IPNETROW[entries];

            // Cycle through the entries.
            for (int index = 0; index < entries; index++)
            {
               // Call PtrToStructure, getting the structure information.
               table[index] = (MIB_IPNETROW) Marshal.PtrToStructure(new
                  IntPtr(currentBuffer.ToInt64() + (index *
                  Marshal.SizeOf(typeof(MIB_IPNETROW)))), typeof(MIB_IPNETROW));
            }

            for (int index = 0; index < entries; index++)
            {
               MIB_IPNETROW row = table[index];
               IPAddress ip=new IPAddress(BitConverter.GetBytes(row.dwAddr));
               Console.Write("IP:"+ip.ToString()+"\t\tMAC:");

               Console.Write( row.mac0.ToString("X2") + '-');
               Console.Write( row.mac1.ToString("X2") + '-');
               Console.Write( row.mac2.ToString("X2") + '-');
               Console.Write( row.mac3.ToString("X2") + '-');
               Console.Write( row.mac4.ToString("X2") + '-');
               Console.WriteLine( row.mac5.ToString("X2"));

            }
         }
         finally
         {
            // Release the memory.
            FreeMibTable(buffer);
         }
      }
   }
}

Solution 2

Hopefully you are trying to get the MAC Addresses from a IP Addresses and not the other way around.

Here is a link of a guy's example:

ARP Resolver

I have not tried it, let us know how it works.

Solution 3

I had a similar problem and wanted to get MAC addresses, given IP addresses for an Asp.Net Core project. I wanted this to work on windows and linux too. As I found no easy to use solution I decided to create a small library called ArpLookup myself (NuGet).

It is able to assign macs to ips on windows and linux. On windows it uses the IpHlpApi.SendARP api. On linux it reads the arp table from /proc/net/arp. If it does not find the ip, it tries pinging it (to froce the OS doing the arp request) and looks in the arp cache again afterwards. This works without taking any dependencies (managed or unmanaged) and without starting processes and parsing their stdout etc..

The windows version is not async, as the underlying API isn't. As the linux version is truly async (async file io for the arp cache + corefx async ping api) I decided to provide an async api anyways and return a finished Task on windows.

It's quite easy to use. A real world usage example is available here.


This is an excerpt of the ARP lookup on windows to map IP -> MAC address:

internal static class ArpLookupService
{
    /// <summary>
    /// Call ApHlpApi.SendARP to lookup the mac address on windows-based systems.
    /// </summary>
    /// <exception cref="Win32Exception">If IpHlpApi.SendARP returns non-zero.</exception>
    public static PhysicalAddress Lookup(IPAddress ip)
    {
        if (ip == null)
            throw new ArgumentNullException(nameof(ip));

        int destIp = BitConverter.ToInt32(ip.GetAddressBytes(), 0);

        var addr = new byte[6];
        var len = addr.Length;

        var res = NativeMethods.SendARP(destIp, 0, addr, ref len);

        if (res == 0)
            return new PhysicalAddress(addr);
        throw new Win32Exception(res);
    }

    private static class NativeMethods
    {
        private const string IphlpApi = "iphlpapi.dll";

        [DllImport(IphlpApi, ExactSpelling = true)]
        [SecurityCritical]
        internal static extern int SendARP(int destinationIp, int sourceIp, byte[] macAddress, ref int physicalAddrLength);
    }
}

The code achieving the same on Linux can be found here. My above-linked library adds a thin abstraction layer that provides one single cross-platform method to do arp lookups like these.

Solution 4

In my case, I wanted to see all ARP broadcast traffic on my network to detect devices broadcasting conflicting IP and MAC addresses on my network. I found "arp -a" polling implementations result in stale information, which makes it particularly challenging to detect IP address conflicts. For instance, two devices were responding to ARP requests, but as one response always arrived later, it would conceal the earlier response in the "arp -a" table.

I used SharpPcap to create a capture service with a capture filter for ARP traffic. Then I use Packet.Net to parse the ARP packets. Finally, I log and generate alerts about IP and MAC address conflicts as the packets come in.

Share:
47,821
BerggreenDK
Author by

BerggreenDK

Started programming as very young on a Commodore VIC-20 and 64. First in BASIC, then moved to 8-bit assembler/machinecode to make things run more smooth. Ended up building a game for it with two friends which we sold and earned Amiga 500 computers. This lead me to the assembler of Motorola 680x0 series CPU. Great CPU btw. Took an IT-education, learned C/C++ and used some Pascal. Build my first networking application for the school I was on, as we needed a cross location chat-tool in DOS. Made it upon Netware/IPX protocol. Then I went and became in love with C++ and its possiblities. Got brainwashed at school, came out saying: Windows, Windows, Windows... so I started to look at DirectX for direct computer power, usable for further multimedia/game development. I've composed a some computer-music too, back in the old C64 days, but thats another story. Later on, I got employed in different companies, including IT-consulting and support. Then worked with advertising/commercial companies as "Multimedia architect/systemdeveloper". I've been all the way through the different browserplatforms and "wars", been building cross platform websites with dynamic HTML from the very early days with Netscape LAYER tags and MSIE DIV, makeing sure that things ALSO worked on Macintosh platoform, so cross platform development has been my goal since 1997. I design and implement SQL-server structures too, so the backend is ready for the frontend. Today I run my own small company, working mostly with .NET and C#, but XHTML/CSS/XML/JQUERY + some Flash Actionscript 3.0 is also part of my work from time to time. Overall - the company is focusing on making the internet easier to learn and use, therefore somethings might be hard to develop, but as long as it will help the user to get respons/results faster and easier, we still go for the "hard way" of developing stuff. Better think before errors happends. I dont have a "language" religion, I dont care what platform we use, as long as its the best for the purpose and we dont have to pay gazillions for licenses. I dont mind paying for quality, but dang - there is really a lot of wanna-be crap out there, so I do check upon things before investing time and money. And no, I dont go for the easy way. I go for the most valueable way, for the customer.

Updated on November 04, 2020

Comments

  • BerggreenDK
    BerggreenDK over 3 years

    I try to figure out which devices are online and which are offline in our LAN.
    I have seen many programs doing a kind of graphical network overview, presenting LAN IP and MAC addresses.

    I would like to know if and how those (ARP?) information can be pulled from C#/.NET ?