【问题标题】:How to query the subnet mask(s) using Mono on Linux?如何在 Linux 上使用 Mono 查询子网掩码?
【发布时间】:2012-02-25 04:20:50
【问题描述】:

在我在开放式嵌入式 Linux 上运行的应用程序(用 MVC3 编写的管理 Web 界面)中,我必须列出所有 TCP/IP 设置。这包括 IP 地址、网关和子网掩码。

以下代码在 MS .Net 下运行良好,但 Mono 2.10 为 "IPv4Mask" 属性抛出 NotImplemntedException:

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

有人知道如何使用 Mono 获取 IPv4 子网掩码吗?

我发现这个问题早在 2009 年就有人问过了,但没有找到任何答案。

【问题讨论】:

  • 要么让他们实现,要么自己实现!
  • 已经在 Mono-List 中询问过,但没有得到任何回应。认为我没有自己实现它的 Linux 专业知识。所以也许其他人知道解决方法。
  • 您可以随时调用ifconfig 并手动解析它。
  • Marc,那个库应该是跨平台的。您可以随时询问漏洞中的开发人员,也许他会要求您重新打开漏洞并提供有关您的特定问题的更多详细信息。
  • 终于在 Mono bugtracker 上注册并提交了一个错误:bugzilla.xamarin.com/show_bug.cgi?id=2033

标签: c# mono network-programming embedded-linux subnet


【解决方案1】:

我查看了一些 Mono 源代码并提取了一些 code-sn-ps 来构建一个帮助程序,该帮助程序返回给定网络接口的 IPv4 子网掩码。该代码不是绝对的美,但它确实有效。

[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;
    }
}

上面helper的用法是这样的:

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

我还没有设法在 Mono 源代码中解决这个问题,因为需要在 Mono 中更改相当多的文件才能从查询它的地方 (LinuxNetworkInterface) 到使用它的地方 (LinuxUnicastIPAddressInfo) 获取上述信息)。但我会将我的代码发布到 Mono 错误报告中,以便 Mono 开发人员可以查看一下。

【讨论】:

    【解决方案2】:

    Marc,只是想对这段代码说 非常感谢。我能够很容易地让它在我的应用程序中运行,并且它在 Mono 2.10.6 上的 Ubuntu 10.10 上运行良好。我希望我能多次对此表示赞同。

    我为 Mono 构建了一个特殊版本的应用程序,因为 Win32 版本使用 System.Deployment,这在 Mono 上不可用;所以作为参考,这是我使用的代码:

    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
    

    注意:nic 只是一个 NetworkInterface 对象,GetUnicastAddrFromNic() 是我创建的一个函数,它只查看 nic 的所有 UnicastAddresses 并返回第一个 AddressFamilyInterNetwork 的函数。

    希望 Mono 团队中的某个人将在即将发布的版本中应用您的解决方案。

    【讨论】:

    • 很好的增强 - 谢谢! Mono:我向 Mono 提交了所有内容,但没有收到他们的任何消息。有点令人失望,但很好......
    • 是的,对于像网络代码这样常见的东西,我希望尽快看到它包含在内,但我想只要跟上 M$ 并让他们的工作代码与 CLR 保持同步就足够了。 . 至少现在我可以着手修复表单上 .NET 和 Mono 渲染元素之间的不一致性,这些元素在 Windows 上运行良好,但在 Linux 上显示不正确。再次感谢。
    猜你喜欢
    • 2016-01-14
    • 1970-01-01
    • 2020-06-16
    • 1970-01-01
    • 2010-12-04
    • 1970-01-01
    • 2016-05-22
    • 2012-12-03
    • 2013-10-10
    相关资源
    最近更新 更多