Skip to content

Instantly share code, notes, and snippets.

@brendanzagaeski
Last active September 19, 2016 16:38
Show Gist options
  • Save brendanzagaeski/9979929 to your computer and use it in GitHub Desktop.
Save brendanzagaeski/9979929 to your computer and use it in GitHub Desktop.
Obtaining the subnet mask of network interfaces on Xamarin.iOS and Xamarin.Mac
// Obtaining the subnet mask of network interfaces on Xamarin.iOS and Xamarin.Mac
//
// On Xamarin.iOS and Xamarin.Mac, `NetworkInterface.OperationalStatus` always
// returns: `OperationalStatus.Unknown`
// https://github.com/mono/mono/blob/f48ceb9860676c342f4c91fbc2e34ea600d839c6/mcs/class/System/System.Net.NetworkInformation/NetworkInterface.cs#L552-L556
//
// Additionally, on all "Linux-like" platforms, including iOS and Mac,
// `UnicastIPAddressInformation.IPv4Mask` is not implemented
// https://github.com/mono/mono/blob/f48ceb9860676c342f4c91fbc2e34ea600d839c6/mcs/class/System/System.Net.NetworkInformation/UnicastIPAddressInformation.cs#L165-L167
//
//
// One workaround is to P/Invoke the `libc` network interface APIs directly.
//
// The following example code will print out the addresses and subnet masks for
// all of the interfaces that are currently up (`ifa_flags` includes `IFF.UP`),
// excluding the loopback device.
using System;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System.Net;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections.Generic;
namespace TestApp
{
// From `mcs/class/System/System.Net.NetworkInformation/MacOsNetworkInterfaceMarshal.cs`
// The original C struct is in `/usr/include/ifaddrs.h`
struct ifaddrs
{
public IntPtr ifa_next;
public string ifa_name;
public uint ifa_flags;
public IntPtr ifa_addr;
public IntPtr ifa_netmask;
public IntPtr ifa_dstaddr;
public IntPtr ifa_data;
}
// The original C struct is in `/usr/include/netinet/in.h`
struct sockaddr_in
{
public byte sin_len;
public byte sin_family;
public ushort sin_port;
public uint sin_addr;
}
// The original C struct is in `/usr/include/sys/socket.h`
struct sockaddr
{
public byte sa_len;
public byte sa_family;
}
// The original C struct is in `/usr/include/net/if_dl.h`
struct sockaddr_dl
{
public byte sdl_len;
public byte sdl_family;
public ushort sdl_index;
public byte sdl_type;
public byte sdl_nlen;
public byte sdl_alen;
public byte sdl_slen;
public byte[] sdl_data;
internal void Read(IntPtr ptr)
{
sdl_len = Marshal.ReadByte(ptr, 0);
sdl_family = Marshal.ReadByte(ptr, 1);
sdl_index = (ushort)Marshal.ReadInt16(ptr, 2);
sdl_type = Marshal.ReadByte(ptr, 4);
sdl_nlen = Marshal.ReadByte(ptr, 5);
sdl_alen = Marshal.ReadByte(ptr, 6);
sdl_slen = Marshal.ReadByte(ptr, 7);
sdl_data = new byte [Math.Max(12, sdl_len - 8)];
Marshal.Copy(new IntPtr(ptr.ToInt64() + 8), sdl_data, 0, sdl_data.Length);
}
}
// The corresponding C defines are in `/usr/include/net/if.h`
[Flags]
enum IFF
{
/* interface is up */
UP = 0x1,
/* broadcast address valid */
BROADCAST = 0x2,
/* turn on debugging */
DEBUG = 0x4,
/* is a loopback net */
LOOPBACK = 0x8,
/* interface is point-to-point link */
POINTOPOINT = 0x10,
/* obsolete: avoid use of trailers */
NOTRAILERS = 0x20,
/* resources allocated */
RUNNING = 0x40,
/* no address resolution protocol */
NOARP = 0x80,
/* receive all packets */
PROMISC = 0x100,
/* receive all multicast packets */
ALLMULTI = 0x200,
/* transmission in progress */
OACTIVE = 0x400,
/* can't hear own transmissions */
SIMPLEX = 0x800,
/* per link layer defined bit */
LINK0 = 0x1000,
/* per link layer defined bit */
LINK1 = 0x2000,
/* per link layer defined bit */
LINK2 = 0x4000,
/* supports multicast */
MULTICAST = 0x8000
}
public partial class TestAppViewController : UIViewController
{
public TestAppViewController() : base("TestAppViewController", null)
{
}
[DllImport("libc")]
static extern int getifaddrs(out IntPtr ifap);
[DllImport("libc")]
static extern void freeifaddrs(IntPtr ifap);
class NetInterfaceInfo
{
public IPAddress Netmask;
public IPAddress Address;
public byte[] MacAddress;
public ushort Index;
public byte Type;
}
const int AF_INET = 2;
const int AF_INET6 = 30;
const int AF_LINK = 18;
// The code in this event handler is essentially a trimmed down version
// of `MacOsNetworkInterface.ImplGetAllNetworkInterfaces()`, from:
// `mcs/class/System/System.Net.NetworkInformation/NetworkInterface.cs`
partial void ButtonClickHandler(NSObject sender)
{
var netInterfaceInfos = new Dictionary<string, NetInterfaceInfo>();
IntPtr ifap;
if (getifaddrs(out ifap) != 0)
throw new SystemException("getifaddrs() failed");
try
{
IntPtr next = ifap;
while (next != IntPtr.Zero)
{
ifaddrs addr = (ifaddrs)Marshal.PtrToStructure(next, typeof(ifaddrs));
if (addr.ifa_addr != IntPtr.Zero && (((IFF)addr.ifa_flags & IFF.UP) != 0) && ((((IFF)addr.ifa_flags) & IFF.LOOPBACK) == 0))
{
byte sa_family = ((sockaddr)Marshal.PtrToStructure(addr.ifa_addr, typeof(sockaddr))).sa_family;
NetInterfaceInfo info;
if (!(netInterfaceInfos.TryGetValue(addr.ifa_name, out info)))
{
info = new NetInterfaceInfo();
netInterfaceInfos.Add(addr.ifa_name, info);
}
if (sa_family == AF_INET)
{
info.Netmask = new IPAddress(((sockaddr_in)Marshal.PtrToStructure(addr.ifa_netmask, typeof(sockaddr_in))).sin_addr);
info.Address = new IPAddress(((sockaddr_in)Marshal.PtrToStructure(addr.ifa_addr, typeof(sockaddr_in))).sin_addr);
}
else if (sa_family == AF_LINK)
{
sockaddr_dl sockaddrdl = new sockaddr_dl();
sockaddrdl.Read(addr.ifa_addr);
info.MacAddress = new byte [(int)sockaddrdl.sdl_alen];
Array.Copy(sockaddrdl.sdl_data, sockaddrdl.sdl_nlen, info.MacAddress, 0, Math.Min(info.MacAddress.Length, sockaddrdl.sdl_data.Length - sockaddrdl.sdl_nlen));
info.Index = sockaddrdl.sdl_index;
info.Type = sockaddrdl.sdl_type;
}
}
next = addr.ifa_next;
}
}
finally
{
freeifaddrs(ifap);
}
var sb = new StringBuilder();
foreach (var netInterfaceInfo in netInterfaceInfos)
{
sb.Append(String.Format("Interface: {0}, netmask: {1}, address: {2}, mac: {3}, index: {4}, type: {5}\n",
netInterfaceInfo.Key,
netInterfaceInfo.Value.Netmask,
netInterfaceInfo.Value.Address,
BitConverter.ToString(netInterfaceInfo.Value.MacAddress),
netInterfaceInfo.Value.Index,
netInterfaceInfo.Value.Type
));
}
MyTextView.Text = sb.ToString();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment