2009-09-06 13 views
6

come devo archiviare correttamente l'elenco di indirizzi IP con gli indirizzi che sono sottoreti per renderlo ricercabile?Come memorizzare l'elenco di indirizzi IP in C# List per renderlo ricercabile anche per le sottoreti?

ci sono due esempi:

  1. ho indirizzo IP 1.2.3.4 e nel mio C# lista c'è 1.2.3.4 ingresso ecco non abbiamo problemi.

  2. Ho l'indirizzo IP 3.4.5.6 e nella mia lista C# ho subnet 3.4.0.0/24. Ecco il mio problema

Come memorizzare la subnet IP in Elenco per coprire il secondo esempio?

Grazie

risposta

4

Alla fine di questa risposta troverete una completa implementazione di una struttura per rappresentare un indirizzo IPv4.

qui è davvero semplice esempio di utilizzo: -

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); 

Il valore "3.4.0.0/255.255.255.0" viene visualizzato inthe console dal 3.4.0.6 si trova nel 3.4.0.0/24 sottorete. Supponendo list è piena di varie sottoreti e x potrebbe contenere qualsiasi indirizzo allora questo: -

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

selezionerà la sottorete più specifico per che 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

Non conservare in un elenco - memorizzano in una struttura come un dizionario, invece, in cui la chiave è l'indirizzo IP, e il valore è l'indirizzo di sottorete.

0

Preferirei creare una struttura specializzata (classe) per archiviare tutte queste informazioni insieme. Probabilmente nel prossimo futuro vorrai estenderlo per memorizzare ipv6 accanto a ipv4, e forse qualche altro dato (metrica, gateway, ecc.).

1

Definire una classe che memorizza un IPAddress e la lunghezza del prefisso:

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

Poi ignorare Equals e GetHashCode tale che solo le prime PrefixLength bit di IPAddress.GetAddressBytes() sono presi in considerazione (e, ovviamente, del tipo IPAddress).

è possibile utilizzare questa classe per memorizzare i prefissi subnet in un List<T> o utilizzarli come tasti di 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" 

Questo funziona con gli indirizzi IPv6, anche.

+0

+1, Ma il costruttore di ipawpl non dovrebbe usare una lunghezza prefissata di 32? Idealmente, dovrebbe essere trovata anche un'istanza costruita con IPAddressWithPrefixLength (IPAddress.Parse ("3.4.5.6"), 32), giusto? –

+0

@Vinay: Non credo, perché la lunghezza del prefisso in 3.4.5.6 è ancora di 16 bit, no? (o 24?) – dtb

+0

@dtb: volevo dire che l'indirizzo di rete (con il prefisso di 16 bit) dovrebbe corrispondere a un indirizzo arbitrario nella sottorete (che potrebbe essere un indirizzo a 32 bit completo o un 24 bit sottorete della rete originale 3.4.0.0). –

0

Potrei usare un albero binario con etichette booleane sui nodi. Usando la notazione standard in cui 0 è il figlio sinistro e 1 il bambino giusto, 1.2.3.4 verrebbe memorizzato mettendo true a 00000001000000100000001100000100 (la rappresentazione binaria di quell'indirizzo) nell'albero - e falso su tutti i nodi tra la radice e questo . Al contrario, 3.4.0.0/16 verrebbe memorizzato con true a 0000001100000100 (i primi 16 bit della rappresentazione binaria di 3.4.0.0).

Quando viene fornito un indirizzo da testare, basta scendere l'albero in base ai bit di tale indirizzo: se si raggiunge un nodo a true, l'indirizzo è presente nell'elenco. Se raggiungi la fine di un ramo, l'indirizzo non è nella lista.

Ad esempio, se si guarda su 3.4.123.48, si scenderebbero 16 livelli nell'albero prima di raggiungere il vero, il che significa che questo indirizzo è nella lista. Ma guardando a 129.199.195.13, sapresti dal primo 1 bit in quell'indirizzo che non fa parte dell'elenco.

Non sono sicuro di quanto sia importante per voi utilizzare il tipo List per archiviare tali indirizzi, quindi questo potrebbe non essere d'aiuto; OTOH, una volta implementato un albero binario di base con etichette, questo dovrebbe avere caratteristiche di prestazione asintotiche migliori rispetto a .Net List.