2009-09-06 19 views
6

¿cómo debo almacenar correctamente la lista de direcciones IP con direcciones que son subredes para que se pueda buscar?¿Cómo almacenar la lista de direcciones IP en la lista C# para que también se pueda buscar en subredes?

Hay dos ejemplos:

  1. tengo la dirección IP 1.2.3.4 y en mi C# lista no es 1.2.3.4 de entrada de modo que aquí no tenemos problemas.

  2. Tengo la dirección IP 3.4.5.6 y en mi lista de C# tengo la subred 3.4.0.0/24. Aquí está mi problema.

Cómo almacenar la subred IP en la lista para cubrir el segundo ejemplo?

Gracias

Respuesta

4

Al final de esta respuesta, encontrará una implementación completa de una estructura para representar una dirección IPV4.

aquí es realmente sencillo ejemplo de utilización: -

List<IPV4Address> list = new List<IPV4Address>(); 
list.Add(IPV4Address.FromString("3.4.0.0", 24)); 
var x = IPV4Address.FromString("3.4.0.6"); 
foreach (var addr in list.Where(a => a.Contains(x))) 
    Console.WriteLine(addr); 

El valor "3.4.0.0/255.255.255.0" se muestra enla consola desde 3.4.0.6 se encuentra en la subred 3.4.0.0/24. Suponiendo list está lleno de diferentes subredes y x podría contener cualquier dirección, entonces esto: -

var result = list.Where(a => a.Contains(x)) 
    .OrderByDescending(a => a.Mask) 
    .FirstOrDefault(); 

seleccionará la subred más específica para que contiene x.

public struct IPV4Address 
{ 
    private UInt32 _Value; 
    private UInt32 _Mask; 

    public UInt32 Value 
    { 
    get { return _Value; } 
    private set { _Value = value; } 
    } 

    public UInt32 Mask 
    { 
    get { return _Mask; } 
    private set { _Mask = value; } 
    } 

    public static IPV4Address FromString(string address) 
    { 
    return FromString(address, 32); 
    } 

    public static IPV4Address FromString(string address, int maskLength) 
    { 
    string[] parts = address.Split('.'); 
    UInt32 value = ((UInt32.Parse(parts[0]) << 24) + 
     ((UInt32.Parse(parts[1])) << 16) + 
     ((UInt32.Parse(parts[2])) << 8) + 
     UInt32.Parse(parts[3])); 

    return new IPV4Address(value, maskLength); 
    } 

    public IPV4Address(UInt32 value) 
    { 
    _Value = value; 
    _Mask = int.MaxValue; 
    } 

    public IPV4Address(UInt32 value, int maskLength) 
    { 
    if (maskLength < 0 || maskLength > 32) 
     throw new ArgumentOutOfRangeException("maskLength", "Must be 0 to 32"); 

    _Value = value; 
    if (maskLength == 32) 
     _Mask = UInt32.MaxValue; 
    else 
     _Mask = ~(UInt32)((1 << (32 - maskLength))-1); 

    if ((_Value & _Mask) != _Value) 
     throw new ArgumentException("Address value must be contained in mask"); 
    } 

    public bool Contains(IPV4Address address) 
    { 
    if ((Mask & address.Mask) == Mask) 
    { 
     return (address.Value & Mask) == Value; 
    } 
    return false; 
    } 

    public override string ToString() 
    { 
    string result = String.Format("{0}.{1}.{2}.{3}", (_Value >> 24), 
     (_Value >> 16) & 0xFF, 
     (_Value >> 8) & 0xFF, 
     _Value & 0xFF); 

    if (_Mask != UInt32.MaxValue) 
     result += "/" + String.Format("{0}.{1}.{2}.{3}", (_Mask >> 24), 
     (_Mask >> 16) & 0xFF, 
     (_Mask >> 8) & 0xFF, 
     _Mask & 0xFF); 

    return result; 
    } 
} 
-1

No conservar en una lista - lo almacenan en una estructura tal como un diccionario en cambio, donde la clave es la dirección IP, y el valor es la dirección de subred.

0

Preferiría crear una estructura (clase) especializada para almacenar toda esta información. Probablemente en un futuro próximo le gustaría extenderlo a la tienda ipv6 junto a ipv4, y tal vez algunos datos más (métrico, puerta de enlace, etc.).

1

Definir una clase que almacena una IPAddress y la longitud de prefijo:

public class IPAddressWithPrefixLength 
{ 
    public IPAddress IPAddress { get; } 
    public int PrefixLength { get; } 
} 

Entonces anular Equals y GetHashCode de tal manera que sólo los primeros PrefixLength bits de IPAddress.GetAddressBytes() se tienen en cuenta (y, por supuesto, el tipo de dirección IP)

continuación, puede utilizar esta clase para almacenar los prefijos de subred en un List<T> o utilizarlos como claves de un Dictionary<K,V>:

var subnets = new List<IPAddressWithPrefixLength> 
{ 
    new IPAddressWithPrefixLength(IPAddress.Parse("1.2.3.4"), 32), 
    new IPAddressWithPrefixLength(IPAddress.Parse("3.4.0.0"), 16), 
}; 

var ipawpl = new IPAddressWithPrefixLength(IPAddress.Parse("3.4.5.6"), 16); 

Console.WriteLine(subnets.Contains(ipawpl)); // prints "True" 

Esto funciona con direcciones IPv6, también.

+0

+1, ¿Pero no debería el constructor de ipawpl usar una longitud de prefijo de 32? Idealmente, una instancia construida con IPAddressWithPrefixLength (IPAddress.Parse ("3.4.5.6"), 32) también debería encontrarse, ¿no? –

+0

@Vinay: No lo creo, porque la longitud del prefijo en 3.4.5.6 sigue siendo de 16 bits, ¿no? (¿o 24?) – dtb

+0

@dtb: Lo que quise decir es que la dirección de red (con el prefijo de 16 bits) debe coincidir con una dirección arbitraria en la subred (que podría ser una dirección completa de 32 bits, o decir una de 24 bits subred de la red 3.4.0.0 original). –

0

Podría usar un árbol binario con etiquetas booleanas en los nodos. Usando la notación estándar en la que 0 es el hijo izquierdo y 1 el secundario correcto, 1.2.3.4 se almacenará poniendo true en 00000001000000100000001100000100 (la representación binaria de esa dirección) en el árbol - y falso en todos los nodos entre la raíz y este . Por el contrario, 3.4.0.0/16 se almacenaría con true en 0000001100000100 (los primeros 16 bits de la representación binaria de 3.4.0.0).

Cuando le den una dirección para probar, simplemente baje por el árbol según los bits de esa dirección: si llega a un nodo a true, la dirección está en la lista. Si llega al final de una sucursal, la dirección no está en la lista.

Por ejemplo, si busca 3.4.123.48, bajará 16 niveles en el árbol antes de llegar a verdadero, lo que significa que esta dirección está en la lista. Pero buscando 129.199.195.13, sabría desde el primer bit de esa dirección que no es parte de la lista.

No estoy seguro de cuán importante es para usted el uso del tipo List para almacenar esas direcciones, por lo que puede que esto no ayude; OTOH, una vez que haya implementado un árbol binario básico con etiquetas, esto debería tener mejores características de rendimiento asintótico que .Net List.

Cuestiones relacionadas