I am attempting to retrieve the MAC address of an IPv6 host in C#. I found this question which mentions using ResolveIpNetEntry2. After some googling, and referencing the MSDN, I haven't found an example of how to call this function from C#. This function is also unlisted on pinvoke.net.
Does anyone have an example of how to call ResolveIpNetEntry2 from C#?
Here is a class showing the structures needed and how to make the call to ResolveIpNetEntry2:
class ResolveMac
{
private const short AF_INET6 = 23;
#region Structs for ResolveIpNetEntry2
struct MIB_IPNET_ROW2
{
public SOCKADDR_INET Address;
public uint InterfaceIndex;
public ulong InterfaceLuid;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] PhysicalAddress;
public uint PhysicalAddressLength;
public NL_NEIGHBOR_STATE State;
public byte Flags;
public uint LastReachable;
}
private struct SOCKADDR_INET
{
public SOCKADDR_IN6 Ipv6;
}
private struct SOCKADDR_IN6
{
public short sin6_family;
public ushort sin6_port;
public uint sin6_flowinfo;
public in6_addr sin6_addr;
public uint sin6_scope_id;
}
struct in6_addr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] Byte;
}
#endregion
private enum NL_NEIGHBOR_STATE
{
NlnsUnreachable,
NlnsIncomplete,
NlnsProbe,
NlnsDelay,
NlnsStale,
NlnsReachable,
NlnsPermanent,
NlnsMaximum
}
[DllImport("Iphlpapi.dll")]
private static extern int ResolveIpNetEntry2(ref MIB_IPNET_ROW2 Row,
ref SOCKADDR_INET SourceAddress);
public static byte[] GetMacFromIPv6Address(IPAddress ipv6Address)
{
if (ipv6Address.AddressFamily !=
System.Net.Sockets.AddressFamily.InterNetworkV6)
throw new ArgumentException(
"The IPAddress provided was not an IPv6 address.");
//set up target address
MIB_IPNET_ROW2 row2 = new MIB_IPNET_ROW2();
row2.PhysicalAddress = new byte[32];
row2.State = NL_NEIGHBOR_STATE.NlnsReachable;
row2.Address.Ipv6.sin6_addr.Byte = new byte[16];
row2.Address.Ipv6.sin6_family = AF_INET6;
row2.Address.Ipv6.sin6_flowinfo = 0;
row2.Address.Ipv6.sin6_port = 0;
row2.Address.Ipv6.sin6_scope_id = Convert.ToUInt32(ipv6Address.ScopeId);
byte[] ipv6AddressBytes = ipv6Address.GetAddressBytes();
System.Buffer.BlockCopy(ipv6AddressBytes, 0,
row2.Address.Ipv6.sin6_addr.Byte, 0, ipv6AddressBytes.Length);
//get this machine's local IPv6 address
SOCKADDR_INET sock = new SOCKADDR_INET();
sock.Ipv6.sin6_family = AF_INET6;
sock.Ipv6.sin6_flowinfo = 0;
sock.Ipv6.sin6_port = 0;
sock.Ipv6.sin6_addr.Byte = new byte[16];
IPAddress[] addresses = Dns.GetHostAddresses(Dns.GetHostName());
foreach (IPAddress address in addresses)
{
if (address.AddressFamily ==
System.Net.Sockets.AddressFamily.InterNetworkV6)
{
sock.Ipv6.sin6_addr.Byte = address.GetAddressBytes();
break;
}
}
foreach (NetworkInterface netInterface in
NetworkInterface.GetAllNetworkInterfaces())
{
if (netInterface.OperationalStatus == OperationalStatus.Up)
{
row2.InterfaceIndex = (uint)clsNetworkStats.GetInterfaceIndex(
netInterface.Description);
break;
}
}
int result = ResolveIpNetEntry2(ref row2, ref sock);
if (result != 0)
throw new ApplicationException(
"The call to ResolveIpNetEntry2 failed; error number: " +
result.ToString());
byte[] macAddress = new byte[6];
System.Buffer.BlockCopy(row2.PhysicalAddress, 0, macAddress, 0, 6);
return macAddress;
}
}
Basically, just ping the target host, and feed the address returned to this function.
The class clsNetworkStats is used to determine the InterfaceIndex that should be used. I found this code example here and modified it slightly.
internal class clsNetworkStats
{
// Fields
private const long ERROR_SUCCESS = 0L;
private ArrayList m_Adapters;
private const long MAX_INTERFACE_NAME_LEN = 0x100L;
private const long MAXLEN_IFDESCR = 0x100L;
private const long MAXLEN_PHYSADDR = 8L;
// Methods
public clsNetworkStats()
: this(true)
{
}
public clsNetworkStats(bool IgnoreLoopBack)
{
int lRetSize = 0;
MIB_IFROW ifrow = new MIB_IFROW();
byte[] buff = new byte[1];
byte val = 0;
long ret = GetIfTable(ref val, ref lRetSize, 0);
buff = new byte[lRetSize];
ret = GetIfTable(ref buff[0], ref lRetSize, 0);
int lRows = buff[0];
this.m_Adapters = new ArrayList(lRows);
byte len = (byte)lRows;
for (byte i = 1; i <= len; i++)
{
ifrow = new MIB_IFROW();
ifrow.dwIndex = Convert.ToUInt32(i);
ret = GetIfEntry(ref ifrow);
IFROW_HELPER ifhelp = this.PrivToPub(ifrow);
if (IgnoreLoopBack)
{
if (ifhelp.Description.IndexOf("Loopback") < 0)
{
this.m_Adapters.Add(ifhelp);
}
}
else
{
this.m_Adapters.Add(ifhelp);
}
}
}
public IFROW_HELPER GetAdapter(int index)
{
return (IFROW_HELPER)this.m_Adapters[index];
}
public int Count
{
get
{
return this.m_Adapters.Count;
}
}
[DllImport("iphlpapi")]
private static extern int GetIfEntry(ref MIB_IFROW pIfRow);
[DllImport("iphlpapi")]
private static extern int GetIfTable(ref byte pIfRowTable, ref int pdwSize, int bOrder);
//[DebuggerStepThrough]
private IFROW_HELPER PrivToPub(MIB_IFROW pri)
{
IFROW_HELPER ifhelp = new IFROW_HELPER();
ifhelp.Name = pri.wszName.Trim();
ifhelp.Index = Convert.ToInt32(pri.dwIndex);
ifhelp.Type = Convert.ToInt32(pri.dwType);
ifhelp.Mtu = Convert.ToInt32(pri.dwMtu);
ifhelp.Speed = Convert.ToInt32(pri.dwSpeed);
ifhelp.PhysAddrLen = Convert.ToInt32(pri.dwPhysAddrLen);
ifhelp.PhysAddr = Encoding.ASCII.GetString(pri.bPhysAddr);
ifhelp.AdminStatus = Convert.ToInt32(pri.dwAdminStatus);
ifhelp.OperStatus = Convert.ToInt32(pri.dwOperStatus);
ifhelp.LastChange = Convert.ToInt32(pri.dwLastChange);
ifhelp.InOctets = pri.dwInOctets; //Convert.ToInt32(pri.dwInOctets);
ifhelp.InUcastPkts = Convert.ToInt32(pri.dwInUcastPkts);
ifhelp.InNUcastPkts = Convert.ToInt32(pri.dwInNUcastPkts);
ifhelp.InDiscards = Convert.ToInt32(pri.dwInDiscards);
ifhelp.InErrors = Convert.ToInt32(pri.dwInErrors);
ifhelp.InUnknownProtos = Convert.ToInt32(pri.dwInUnknownProtos);
ifhelp.OutOctets = pri.dwOutOctets;//Convert.ToInt32(pri.dwOutOctets);
ifhelp.OutUcastPkts = Convert.ToInt32(pri.dwOutUcastPkts);
ifhelp.OutNUcastPkts = Convert.ToInt32(pri.dwOutNUcastPkts);
ifhelp.OutDiscards = Convert.ToInt32(pri.dwOutDiscards);
ifhelp.OutErrors = Convert.ToInt32(pri.dwOutErrors);
ifhelp.OutQLen = Convert.ToInt32(pri.dwOutQLen);
ifhelp.Description = Encoding.ASCII.GetString(pri.bDescr, 0, Convert.ToInt32(pri.dwDescrLen));
ifhelp.InMegs = this.ToMegs((long)ifhelp.InOctets);
ifhelp.OutMegs = this.ToMegs((long)ifhelp.OutOctets);
return ifhelp;
}
[DebuggerStepThrough]
private string ToMegs(long lSize)
{
string sDenominator = " B";
if (lSize > 0x3e8L)
{
sDenominator = " KB";
lSize = (long)Math.Round((double)(((double)lSize) / 1000.0));
}
else if (lSize <= 0x3e8L)
{
sDenominator = " B";
// lSize = lSize;
}
return lSize.ToString("###,###") + sDenominator;
// (Strings.Format(lSize, "###,###0") + sDenominator);
}
// Nested Types
[StructLayout(LayoutKind.Sequential)]
public struct IFROW_HELPER
{
public string Name;
public int Index;
public int Type;
public int Mtu;
public int Speed;
public int PhysAddrLen;
public string PhysAddr;
public int AdminStatus;
public int OperStatus;
public int LastChange;
public uint InOctets; //changed
public int InUcastPkts;
public int InNUcastPkts;
public int InDiscards;
public int InErrors;
public int InUnknownProtos;
public uint OutOctets; //changed
public int OutUcastPkts;
public int OutNUcastPkts;
public int OutDiscards;
public int OutErrors;
public int OutQLen;
public string Description;
public string InMegs;
public string OutMegs;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct MIB_IFROW
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x100)]
public string wszName;
public uint dwIndex;
public uint dwType;
public uint dwMtu;
public uint dwSpeed;
public uint dwPhysAddrLen;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] bPhysAddr;
public uint dwAdminStatus;
public uint dwOperStatus;
public uint dwLastChange;
public uint dwInOctets;
public uint dwInUcastPkts;
public uint dwInNUcastPkts;
public uint dwInDiscards;
public uint dwInErrors;
public uint dwInUnknownProtos;
public uint dwOutOctets;
public uint dwOutUcastPkts;
public uint dwOutNUcastPkts;
public uint dwOutDiscards;
public uint dwOutErrors;
public uint dwOutQLen;
public uint dwDescrLen;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x100)]
public byte[] bDescr;
}
public static int GetInterfaceIndex(string description)
{
int val = 0;
clsNetworkStats stats = new clsNetworkStats();
for (int index = 0; index < stats.Count; index++)
{
string desc = stats.GetAdapter(index).Description;
if (desc == description + "\0")
{
val = stats.GetAdapter(index).Index;
break;
}
}
return val;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With