5

In my application (admin web interface written in MVC3) running on open-embedded Linux I have to list all the TCP/IP settings. This includes IP-Adresse, Gateway and the subnet mask.

The following code runs well under MS .Net but Mono 2.10 throws a NotImplemntedException for the "IPv4Mask" property:

var ipProperties = networkIntf.GetIPProperties(); 
var unicastIpInfo = ipProperties.UnicastAddresses.FirstOrDefault(); 
var subnetMask = unicastAddress != null ? unicastAddress.IPv4Mask.ToString() : ""; 

Does anybody know how one can get the IPv4 subnet mask using Mono?

I found this question was asked already in 2009 but didn't find any answer to it.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Marc
  • 4,715
  • 3
  • 27
  • 34
  • Either ask them to implement it, or implement it yourself! – leppie Nov 14 '11 at 09:24
  • Asked already in the Mono-List but didn't got any response. Think I don't have the Linux know-how to implement it myself. So maybe somebody else knows a workaround. – Marc Nov 14 '11 at 09:26
  • 1
    You can always just call `ifconfig` and parse it manually. – leppie Nov 14 '11 at 09:28
  • Thats a workaround - thx. The problem is that it is platform specific. I hoped there is a platform independent API in Mono that IS actually implemented even if it wouldn't make too much sense to have multiple API ... just hoping ... ;-) – Marc Nov 14 '11 at 09:34
  • Found an old bug-report and fix for this on Novells bugtracker: https://bugzilla.novell.com/show_bug.cgi?id=416526 Sounds like somebody fixed it - at least for the Windows platform. Still doesn't work on Linux. So maybe there is only the above workaround left :-/ – Marc Nov 14 '11 at 10:04
  • 2
    Marc, that library should be cross-platform. You could always ask the developer in the bug, and maybe he asks you to reopen the bug and provide more details about your particular problem. – knocte Nov 14 '11 at 13:25
  • @Marc When did you ask the mono-list, I've not noticed it – IanNorton Nov 15 '11 at 00:10
  • Mono-List: I registred and asked using the web UI but just saw (in another Web UI they have) that my questions where never accepted for the list: http://mono.1490590.n4.nabble.com/How-to-get-the-IPv4-subnet-mask-using-Mono-2-10-on-OpenEmbedded-Linux-td3990069.html#none Then I found the above bug on Novells bugtracker but not on the current mono bugtracker (which seems not the have the old data/bug). Other links I found on the Mono-website pointed to "page-not-found". – Marc Nov 15 '11 at 07:22
  • Reporting a bug on the mono bugtracker seems to be a major task - at least for me. The first page lists tons of components and modules where I don't even know to log a bug. ...all not really straight forward... ;-( In the meanwhile I've implemented the workaround with ifconfig but have to test it next time when I can access to the embedded Linux. – Marc Nov 15 '11 at 07:25
  • 1
    Finally managed to get registred at the Mono bugtracker and filed a bug: http://bugzilla.xamarin.com/show_bug.cgi?id=2033 – Marc Nov 15 '11 at 08:19

2 Answers2

5

I took a look at some Mono sourcecode and extracted some code-snippets to build a helper that returns a the IPv4 subnet mask of the given network interface. The code is not an absolute beauty but it works.

[StructLayout(LayoutKind.Explicit)]
struct ifa_ifu
{
    [FieldOffset(0)]
    public IntPtr ifu_broadaddr;

    [FieldOffset(0)]
    public IntPtr ifu_dstaddr;
}

[StructLayout(LayoutKind.Sequential)]
struct ifaddrs
{
    public IntPtr ifa_next;
    public string ifa_name;
    public uint ifa_flags;
    public IntPtr ifa_addr;
    public IntPtr ifa_netmask;
    public ifa_ifu ifa_ifu;
    public IntPtr ifa_data;
}

[StructLayout(LayoutKind.Sequential)]
struct sockaddr_in
{
    public ushort sin_family;
    public ushort sin_port;
    public uint sin_addr;
}

[StructLayout(LayoutKind.Sequential)]
struct sockaddr_in6
{
    public ushort sin6_family;   /* AF_INET6 */
    public ushort sin6_port;     /* Transport layer port # */
    public uint sin6_flowinfo; /* IPv6 flow information */
    public in6_addr sin6_addr;     /* IPv6 address */
    public uint sin6_scope_id; /* scope id (new in RFC2553) */
}

[StructLayout(LayoutKind.Sequential)]
struct in6_addr
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
    public byte[] u6_addr8;
}

[StructLayout(LayoutKind.Sequential)]
struct sockaddr_ll
{
    public ushort sll_family;
    public ushort sll_protocol;
    public int sll_ifindex;
    public ushort sll_hatype;
    public byte sll_pkttype;
    public byte sll_halen;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public byte[] sll_addr;
}

internal class IPInfoTools
{
    const int AF_INET = 2;
    const int AF_INET6 = 10;
    const int AF_PACKET = 17;

    [DllImport("libc")]
    static extern int getifaddrs (out IntPtr ifap);

    [DllImport ("libc")]
    static extern void freeifaddrs (IntPtr ifap);

    internal static string GetIPv4Mask(string networkInterfaceName)
    {
        IntPtr ifap;
        if (getifaddrs(out ifap) != 0)
        {
            throw new SystemException("getifaddrs() failed");
        }

        try
        {
            var next = ifap;
            while (next != IntPtr.Zero)
            {
                var addr = (ifaddrs)Marshal.PtrToStructure(next, typeof(ifaddrs));
                var name = addr.ifa_name;

                if (addr.ifa_addr != IntPtr.Zero)
                {
                    var sockaddr = (sockaddr_in)Marshal.PtrToStructure(addr.ifa_addr, typeof(sockaddr_in));
                    switch (sockaddr.sin_family)
                    {
                        case AF_INET6:
                            //sockaddr_in6 sockaddr6 = (sockaddr_in6)Marshal.PtrToStructure(addr.ifa_addr, typeof(sockaddr_in6));
                            break;
                        case AF_INET:
                            if (name == networkInterfaceName)
                            {
                                var netmask = (sockaddr_in)Marshal.PtrToStructure(addr.ifa_netmask, typeof(sockaddr_in));
                                var ipAddr = new IPAddress(netmask.sin_addr);  // IPAddress to format into default string notation
                                return ipAddr.ToString();
                            }
                            break;
                        case AF_PACKET:
                            {
                                var sockaddrll = (sockaddr_ll)Marshal.PtrToStructure(addr.ifa_addr, typeof(sockaddr_ll));
                                if (sockaddrll.sll_halen > sockaddrll.sll_addr.Length)
                                {
                                    Console.Error.WriteLine("Got a bad hardware address length for an AF_PACKET {0} {1}",
                                                            sockaddrll.sll_halen, sockaddrll.sll_addr.Length);
                                    next = addr.ifa_next;
                                    continue;
                                }
                            }
                            break;
                    }
                }

                next = addr.ifa_next;
            }
        }
        finally
        {
            freeifaddrs(ifap);
        }

        return null;
    }
}

Usage of the above helper is like this:

String subnetMask = IPInfoTools.GetIPv4Mask("etc0");

I didn't yet manage to fix this in the Mono sourcecode as one needs to change quite some files in Mono to get the above information from the place where it is queried (LinuxNetworkInterface) to the place where it is used (LinuxUnicastIPAddressInfo). But I will post my code to the Mono bug-report so maybe one of the Mono developers can take a look.

Marc
  • 4,715
  • 3
  • 27
  • 34
1

Marc, just wanted to say THANK YOU VERY MUCH for this code. I was able to get it working in my application very easily and it works great on Ubuntu 10.10 on Mono 2.10.6. I wish I could upvote this more than once.

I build a special version of the app for Mono since the Win32 version uses System.Deployment which is not available on Mono; so for reference, here is the code I used:

UnicastIPAddressInformation addr = GetUnicastAddrFromNic(nic.GetIPProperties().UnicastAddresses);

#if BUILD4MONO
string mask = null;
try {
    mask = IPInfoTools.GetIPv4Mask(nic.Name);  // Marc's function - nic.Name is eth0 or wlan1 etc.
} catch (Exception ex) { // getifaddrs failed
   Console.WriteLine("GetIPRangeInfoFromNetworkInterface failed: {0}", ex.Message);
}

if (mask == null || IPAddress.TryParse(mask, out rangeInfo.SubnetMask) == false) {
    rangeInfo.SubnetMask = IPAddress.Parse("255.255.255.0"); // default to this
}
#else
rangeInfo.SubnetMask = addr.IPv4Mask;  // Win32
#endif

Note: nic is just a NetworkInterface object and GetUnicastAddrFromNic() is a function I made that just looks at all the UnicastAddresses for the nic and returns the first one whose AddressFamily is InterNetwork.

Hopefully someone from the Mono team will apply your solution in an upcoming version.

drew010
  • 68,777
  • 11
  • 134
  • 162
  • Nice enhancement - thanks! Mono: I submitted all to Mono but didn't hear anything from them. Kind of disappointing but well... – Marc Feb 25 '12 at 06:47
  • 1
    Yeah for something as common as networking code I would like to see it included ASAP, but I suppose its hard enough just keeping up with M$ and keeping their working code up to date with the CLR... At least now I can move onto fixing the inconsistencies between .NET and Mono rendering elements on my form that work fine on Windows but show incorrectly on Linux. Thanks again. – drew010 Feb 25 '12 at 19:21