c# - how - update mono
¿Cómo consultar las máscaras de subred usando Mono en Linux? (2)
En mi aplicación (interfaz web de administración escrita en MVC3) que se ejecuta en Linux incrustado, tengo que enumerar todas las configuraciones de TCP / IP. Esto incluye IP-Adresse, Gateway y la máscara de subred.
El siguiente código se ejecuta bien bajo MS .Net pero Mono 2.10 arroja una excepción NotImplemntedException para la propiedad "IPv4Mask"
:
var ipProperties = networkIntf.GetIPProperties();
var unicastIpInfo = ipProperties.UnicastAddresses.FirstOrDefault();
var subnetMask = unicastAddress != null ? unicastAddress.IPv4Mask.ToString() : "";
¿Alguien sabe cómo se puede obtener la máscara de subred IPv4 usando Mono?
Descubrí que esta pregunta ya se hizo en 2009, pero no encontré ninguna respuesta.
Eché un vistazo a algún código fuente Mono y extraje algunos fragmentos de código para construir un helper que devuelve una máscara de subred IPv4 de la interfaz de red dada. El código no es una belleza absoluta, pero funciona.
[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;
}
}
El uso del asistente anterior es así:
String subnetMask = IPInfoTools.GetIPv4Mask("etc0");
Todavía no logré solucionar esto en el código fuente Mono ya que uno necesita cambiar bastante archivos en Mono para obtener la información anterior del lugar donde se consulta (LinuxNetworkInterface) al lugar donde se usa (LinuxUnicastIPAddressInfo). Pero publicaré mi código en el informe de error de Mono para que tal vez uno de los desarrolladores de Mono lo pueda ver.
Marc, solo quería decir MUCHAS GRACIAS por este código. Pude hacerlo funcionar en mi aplicación muy fácilmente y funciona muy bien en Ubuntu 10.10 en Mono 2.10.6. Desearía poder votar esto más de una vez.
Construyo una versión especial de la aplicación para Mono ya que la versión de Win32 usa System.Deployment
que no está disponible en Mono; para referencia, aquí está el código que utilicé:
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
Nota: nic
es solo un objeto NetworkInterface
y GetUnicastAddrFromNic()
es una función que hice que solo mira todos los UnicastAddresses para el nic y devuelve el primero cuya AddressFamily
es InterNetwork
.
Con suerte, alguien del equipo de Mono aplicará su solución en una próxima versión.