C#. Как определить, какие IP-адреса в списке принадлежат определенной подсети?

У меня есть большой динамический список IP-адресов, их масок подсети и заданный набор подсетей. Мне нужен код, чтобы определить, в какой подсети находится каждый адрес. Я держу это довольно расплывчатым, потому что я открыт для многих различных решений. У кого-нибудь есть код на С#, который может сделать что-то подобное? Спасибо!


person MyOtherCarIsEpona    schedule 13.09.2011    source источник
comment
Помогает ли это Как ответить[1]? [1]: заголовок stackoverflow.com/questions/1499269/   -  person Iain Hoult    schedule 13.09.2011
comment
@Iain Hoult: Это на самом деле дубликат предыдущего :)   -  person leppie    schedule 13.09.2011
comment
Не дубликат, если вы читаете подробности.   -  person Jonathan Dickinson    schedule 13.09.2011


Ответы (3)


Во-первых, вам нужен код для маскировки адреса в его подсети:

public IPAddress ApplyMask(IPAddress address, IPAddress mask) {
    byte[] addressBytes = address.GetAddressBytes();
    byte[] maskBytes = mask.GetAddressBytes();
    var appliedMaskBytes =
        addressBytes.Zip(
            maskBytes,
            (addressByte, maskByte) => (byte)(addressByte & maskByte)
        )
        .ToArray();
    return new IPAddress(appliedMaskBytes);
}

Затем, если у вас есть Dictionary<IPAddress, IPAddress> от IP-адреса до его маски:

IDictionary<IPAddress, IPAddress> maskDictionary;
var masked = ipAddresses
                 .Select(ipAddress => 
                     new { IPAddress = ipAddress,
                           Subnet = ApplyMask(
                               ipAddress,
                               maskDictionary[ipAddress]
                           )
                     }
                 )
                 .GroupBy(x => x.Subnet);

Теперь у вас есть список адресов по подсети. Действуйте соответственно.

(Примечание: у меня нет под рукой компилятора. Извините за мелкие ошибки компилятора.)

person jason    schedule 13.09.2011
comment
Не беспокойтесь об ошибках, это должно помочь мне начать. Я ценю помощь! (Я бы проголосовал за вас, но у меня недостаточно репутации.) Я посмотрю, сработает ли это сегодня, и отчитаюсь. - person MyOtherCarIsEpona; 13.09.2011
comment
Другой вопрос - addressBytes.Zip - у меня проблемы с поиском этого класса. Вы случайно не знаете, в какой библиотеке она находится? - person MyOtherCarIsEpona; 13.09.2011
comment
Моя первоначальная реализация сделала бы это; но я понял, что {192.168.0.0, 255.255.255.0} это не то же самое, что {192.168.0.0, 255.255.0.0} - согласно этой реализации это так. - person Jonathan Dickinson; 13.09.2011
comment
@MyOtherCarIsEpona: это в .NET 4.0; убедитесь, что вы используете System.Linq. - person jason; 13.09.2011

Вам не нужен список подсетей - их можно вывести. Вы можете сделать это с помощью побитовой математики:

// Key is {Subnet, Subnet Mask}.
// Value is list of IPs in that range.
public static IDictionary<Tuple<IPAddress, IPAddress>, ISet<IPAddress>> AllocateToSubnets(IEnumerable<Tuple<IPAddress, IPAddress>> ips)
{
    var dic = new Dictionary<Tuple<IPAddress, IPAddress>, ISet<IPAddress>>();
    foreach (var item in ips)
    {
        var subnet = Tuple.Create(GetSubnetAddress(item.Item1, item.Item2), item.Item2);

        ISet<IPAddress> set;
        if (!dic.TryGetValue(subnet, out set))
            dic.Add(subnet, set = new HashSet<IPAddress>());
        if (!set.Add(item.Item1))
        {
            // Throw an error if you are looking for duplicates.
        }
    }

    return dic;
}

private static IPAddress GetSubnetAddress(IPAddress address, IPAddress mask)
{
    var b1 = address.GetAddressBytes();
    var b2 = mask.GetAddressBytes();
    if (b1.Length != b2.Length)
        throw new InvalidOperationException("IPAddress and its mask do not share the same family.");
    for (var i = 0; i < b1.Length; i++)
        b1[i] &= b2[i];
    return new IPAddress(b1);
}

Тестовый код:

var ips = new[]
    {
        Tuple.Create(IPAddress.Parse("192.168.0.1"), IPAddress.Parse("255.255.255.0")),
        Tuple.Create(IPAddress.Parse("192.168.0.1"), IPAddress.Parse("255.255.0.0")),
        Tuple.Create(IPAddress.Parse("192.168.0.2"), IPAddress.Parse("255.255.255.0")),
        Tuple.Create(IPAddress.Parse("192.168.0.10"), IPAddress.Parse("255.255.255.0")),
        Tuple.Create(IPAddress.Parse("192.168.0.10"), IPAddress.Parse("255.255.0.0")),
        Tuple.Create(IPAddress.Parse("10.0.0.1"), IPAddress.Parse("255.255.255.0")),
        Tuple.Create(IPAddress.Parse("10.1.0.1"), IPAddress.Parse("255.255.0.0")),
        Tuple.Create(IPAddress.Parse("10.0.0.2"), IPAddress.Parse("255.255.255.0")),
        Tuple.Create(IPAddress.Parse("10.1.0.10"), IPAddress.Parse("255.255.255.0")),
        Tuple.Create(IPAddress.Parse("10.1.0.10"), IPAddress.Parse("255.255.0.0")),
    };

var subnets = AllocateToSubnets(ips);
person Jonathan Dickinson    schedule 13.09.2011

Это зависит от того, что для определения вашей подсети имеет. Если это диапазон IP-адресов, который вы можете использовать: Самый простой способ проверить IP-адрес по диапазону значений с помощью c#

Если это маска, вы можете использовать ip&(~mask) для получения нижнего значения и ip|(~mask) для получения диапазона и действовать, как указано выше.

Если подсеть указана с количеством битов, которые имеет сетевая маска, вы можете рассчитать: mask=~(UInt32.MaxValue>>n) и использовать действуйте, как указано выше.

Если вы хотите узнать сетевую маску для данного IP, то я понятия не имею. Про IPv6 тоже не знаю.

person CodesInChaos    schedule 13.09.2011
comment
Итак, вы говорите, что я должен просто выполнить арифметические действия с адресом и маской, чтобы найти диапазон, а затем взять первый адрес в этом диапазоне? - person MyOtherCarIsEpona; 13.09.2011
comment
Ваш вопрос настолько расплывчатый, что трудно понять, чего вы хотите. - person CodesInChaos; 13.09.2011
comment
Прости. Я предполагаю, что первый адрес в диапазоне подсети данного IP - это то, что мне нужно. - person MyOtherCarIsEpona; 13.09.2011