2011-11-14 3 views
5

В моем приложении (административный веб-интерфейс, написанный на MVC3), запущенном на открытой встроенной Linux, я должен перечислить все настройки TCP/IP. Это включает в себя IP-адрес, шлюз и маску подсети.Как запросить маску подсети с помощью Mono в Linux?

Следующий код работает хорошо под MS .Net, но Mono 2,10 бросает NotImplemntedException для "IPv4Mask" собственности:

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

Кто-нибудь знает, как можно получить маску подсети IPv4 с помощью Mono?

Я нашел этот вопрос задан уже в 2009 году, но не нашел ответа на него.

+0

Либо попросите их реализовать его, либо реализовать его самостоятельно! – leppie

+0

Вопрос уже задан в Mono-List, но ответа не получил. Думаю, что у меня нет ноу-хау Linux для его реализации. Так что, возможно, кто-то еще знает обходное решение. – Marc

+1

Вы всегда можете просто вызвать 'ifconfig' и проанализировать его вручную. – leppie

ответ

5

Я взглянул на некоторый код исходного кода Mono и извлек некоторые фрагменты кода для создания помощника, который возвращает маску подсети 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; 
    } 
} 

Использование вышеуказанного помощника, как это:

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

Я еще не успел исправить это в исходный код Mono, как нужно изменить довольно некоторые файлы в моно, чтобы получить выше информацию из того места, где она запрашивается (LinuxNetworkInterface), туда, где она используется (LinuxUnicastIPAddressInfo). Но я отправлю свой код в отчет об ошибке Mono, поэтому, возможно, один из разработчиков Mono может посмотреть.

1

Марк, просто хотел сказать СПАСИБО ОЧЕНЬ для этого кода. Мне удалось легко работать с ним в моем приложении, и он отлично работает на Ubuntu 10.10 на Mono 2.10.6. Мне жаль, что я не смог бы это сделать.

Я создаю специальную версию приложения для 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() функции Я сделал, что только смотрит на всех UnicastAddresses сетевой платы и возвращает первый, чей AddressFamily есть InterNetwork.

Надеюсь, кто-то из команды Mono применит ваше решение в следующей версии.

+0

Хорошее улучшение - спасибо! Моно: я отправил все в Mono, но ничего не слышал от них. Какой-то неутешительный, но хорошо ... – Marc

+1

Да, для чего-то такого же, как сетевой код, я хотел бы, чтобы он включал ASAP, но я полагаю, что его достаточно сложно поддерживать M $ и обновлять рабочий код с CLR. .. По крайней мере, теперь я могу перейти к исправлению несоответствий между элементами рендеринга .NET и Mono в моей форме, которые отлично работают в Windows b ut неправильно отображаются в Linux. Еще раз спасибо. – drew010

Смежные вопросы