2

I'm trying to leverage the power of the ARP protocol in (vb) .NET to build a list of all networked devices on my home LAN as well as their hardware (MAC) address. I already figured out how to calculate all IP addresses on a subnet but I'm stuck finding a clean way to send an ARP request and read the response.

Note that all networked devices includes non-windows devices (such as smartphones). This means WMI is out of the question.

Now I don't know which way is more cumbersome: A) wrapping the native IP helper API in managed code in order to use its SendArp method, or B) sending a ping request to each host in order to fill the ARP cache and then parsing the output from arp.exe -a.

Does anyone know a way to handle ARP requests/responses using only managed code? It doesn't seem very difficult to implement using socket objects and a little sprinkle of magic, but I couldn't find any third-party networking libraries that offer an ARP API. I'm afraid I'm not knowledgeable enough to create one myself.

Steven Liekens
  • 13,266
  • 8
  • 59
  • 85

3 Answers3

1

So I've decided that wrapping the windows API would be the least messy solution and came up with the following for all to see:

First I created a Friend NotInheritable Class with a private constructor named NativeMethods (roughly the equivalent of a static internal class in C#) with a subclass (also static internal) named IPHelper. This is where I put the DllImport which I shamelessly copied from pinvoke.net (source).

Friend NotInheritable Class NativeMethods
    Private Sub New()
    End Sub

    Friend NotInheritable Class IPHelper
            Private Sub New()
            End Sub

            ' Possible return values
            Friend Const NO_ERROR As Integer = 0
            Friend Const ERROR_BAD_NET_NAME As Integer = 67
            Friend Const ERROR_BUFFER_OVERFLOW As Integer = 111
            Friend Const ERROR_GEN_FAILURE As Integer = 31
            Friend Const ERROR_INVALID_PARAMETER As Integer = 87
            Friend Const ERROR_INVALID_USER_BUFFER As Integer = 1784
            Friend Const ERROR_NOT_FOUND As Integer = 1168
            Friend Const ERROR_NOT_SUPPORTED As Integer = 50

            ' API function declaration.
            <DllImport("iphlpapi.dll", SetLastError:=True)>
            Friend Shared Function SendARP(
                     DestIP As UInt32,
                     SrcIP As UInt32,
                     pMacAddr() As Byte,
                     ByRef PhyAddrLen As Int32) As UInt32
            End Function

    End Class
End Class

Now on top of that I wrote a public class ArpRequest which consumes the SendARP method.

Imports System.Net
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports System.IO
Imports System.Net.NetworkInformation

Public Class ArpRequest

    Private _address As IPAddress

    Public Sub New(address As IPAddress)
            _address = address
    End Sub

    ''' <summary>
    ''' Gets the MAC address that belongs to the specified IP address.
    ''' </summary>
    ''' <remarks>This uses a native method and should be replaced when a managed alternative becomes available.</remarks>
    Public Function GetResponse() As PhysicalAddress
            Dim ip As UInteger = BitConverter.ToUInt32(_address.GetAddressBytes(), 0)
            Dim mac() As Byte = New Byte(5) {}

            Dim ReturnValue As Integer = CInt(NativeMethods.IPHelper.SendARP(CUInt(ip), 0, mac, mac.Length))

            If ReturnValue = NativeMethods.IPHelper.NO_ERROR Then
                    Return New PhysicalAddress(mac)
            Else
                    ' TODO: handle various SendARP errors
                    ' http://msdn.microsoft.com/en-us/library/windows/desktop/aa366358(v=vs.85).aspx
                    Throw New Win32Exception(CInt(ReturnValue))
            End If
    End Function
End Class

Usage is simple (but watch out for Win32Exceptions):

    Dim ip = System.Net.IPAddress.Parse("0.0.0.0") ' replace with actual ip
    Dim arp = New ArpRequest(ip)
    Dim hardwareAddress = arp.GetResponse()
Steven Liekens
  • 13,266
  • 8
  • 59
  • 85
0

Related:

The accepted answer uses COM, but cited there is also a quite interesting link to a developer who appears to have created a C#-based packet capture library, and example of how it can be used to get ARP information. Maybe that will work for you?

Note that I do not know if "SharpPcap" wraps unmanaged code.

There is no built-in managed code that can deal directly with ARP AFAIK.

live2
  • 3,771
  • 2
  • 37
  • 46
pseudocoder
  • 4,314
  • 2
  • 25
  • 40
0

I created such a library you were asking for: ArpLookup.

Actually you can't use a socket object to produce ARP packets as raw sockets are disabled/admin-only/etc. in all relevant modern OSes. Because I wanted to write cross-platform code I implemented both of your ideas as one fit's Linux better, one Windows. On Windows the native API is used. On Linux the /proc/net/arp file containing the system's arp table is read. If the asked-for IP is not found, a ping is sent and the lookup is done again. This works on Linux incl. Android (given relevant app permissions) but not on macOS.

Georg Jung
  • 949
  • 10
  • 27