tags:

views:

2978

answers:

2

I am trying to figure out what devices are online and offline in our LAN. I've 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 ?

Any sample code snippets/links would be appreciated.

+5  A: 

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);

      // 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++)
            {

               IPAddress ip=new IPAddress(table[index].dwAddr);
               Console.Write("IP:"+ip.ToString()+"\t\tMAC:");
               byte b;

               b=table[index].mac0;
               if ( b < 0x10)
               {
                  Console.Write("0");
               }
               else
               {
                  Console.Write("");
               }
               Console.Write(b.ToString("X"));

               b=table[index].mac1;
               if ( b < 0x10)
               {
                  Console.Write("-0");
               }
               else
               {
                  Console.Write("-");
               }
               Console.Write(b.ToString("X"));

               b=table[index].mac2;
               if ( b < 0x10)
               {
                  Console.Write("-0");
               }
               else
               {
                  Console.Write("-");
               }
               Console.Write(b.ToString("X"));

               b=table[index].mac3;
               if ( b < 0x10)
               {
                  Console.Write("-0");
               }
               else
               {
                  Console.Write("-");
               }
               Console.Write(b.ToString("X"));

               b=table[index].mac4;
               if ( b < 0x10)
               {
                  Console.Write("-0");
               }
               else
               {
                  Console.Write("-");
               }
               Console.Write(b.ToString("X"));

               b=table[index].mac5;
               if ( b < 0x10)
               {
                  Console.Write("-0");
               }
               else
               {
                  Console.Write("-");
               }
               Console.Write(b.ToString("X"));
               Console.WriteLine();
            }
         }
         finally
         {
            // Release the memory.
            Marshal.FreeCoTaskMem(buffer);
         }
      }
   }
}
Rex Logan
Sorry, but the idea was to discover which machines are online currently. We dont know the IP at the moment they connect, as they might come from wireless and get the IP via DHCP. I know that we could lock the routers, but the idea was to look for the MAC address from the ARP + gather the current list of devices attached to the network.
BerggreenDK
Do you know how to get the MAC address from an IP-number?
BerggreenDK
Great stuff! This I gotta test out. I have marked it as answer now. Thanks a lot! Brings me forward... "one giant leap" :o)
BerggreenDK
A: 

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.

Jonathan.Peppers
Thanks for the link, but would that example not need the following:using Tamir.IPLib;using Tamir.IPLib.Packets;using Tamir.IPLib.Util;???
BerggreenDK
I am also trying to find out how/if its possible to make a "C#" version of commandprompt "arp -a" ... not by calling a hidden commandprompt, but merly by doing the ARP command by code somehow.As I've understood so far, the ARP command lists the current available IP+their MAC addresses seen from "this network card"... and that would suit our needs perfectly.
BerggreenDK
The ARP command is sending raw bytes over a socket to accomplish this. The class in the link can resolve a MAC Address from an IP Address, is this what you need, or do you need to somehow "discover" IP Addresses? The using statements at the top come from SharpPcap.dll which is downloadable and open source from the link I posted above.
Jonathan.Peppers
I also wanted to mention that the ARP table stored with your network card is not necessarily up to date all of the time. It can refresh randomly and transparently before sending TCP/IP traffic. Depending on what you're trying to do, there might be a better way to accomplish it.
Jonathan.Peppers
Well, first of all I want to discover what Network cards are "online" in the same IP segment as the "service I am trying to construct". When I have a list of the active IP's or those communicating on the network, I want to lookup their respective MAC numbers to keep some sort of "simple" validation of who has which IP right now.Its not a safe solution I know, but I want to make a dynamic list of MAC-numbers/users/machines online currently, so the servers can activate certain "tunnels" or "services" accordingly and shut the rest down.
BerggreenDK