Вычисление всех адресов в подсети ... для IPv6 - PullRequest
7 голосов
/ 16 августа 2011

Я видел множество замечательных C # примеров , которые демонстрируют, как преобразовать адреса IPv4, представленные в нотации CIDR (например, 192.168.0.1/25), в соответствующие им диапазоны (192.168.0.1 - 192.168.0.126). Моя программа должна быть в состоянии сделать это (чтобы вычислить все адреса в моей локальной подсети), но я также хочу поддерживать IPv6.

Если в моей программе на C # есть вся моя типичная информация ipconfig (IPv4-адрес, маска подсети, IPv6-адрес, link-local v6-адрес, шлюз по умолчанию) - как бы мне создать список всех IPv6-адресов в моем локальной подсети и выводом их на консоль?

Ответы [ 4 ]

9 голосов
/ 16 августа 2011

Вы можете использовать класс eExNetworkLibrary.IP.IPAddressAnalysis из eExNetworkLibrary .

Следующий код работает с IPv4 и IPv6 (только что протестирован).

        string strIn = "2001:DB8::/120";

        //Split the string in parts for address and prefix
        string strAddress = strIn.Substring(0, strIn.IndexOf('/'));
        string strPrefix = strIn.Substring(strIn.IndexOf('/') + 1);

        int iPrefix = Int32.Parse(strPrefix);
        IPAddress ipAddress = IPAddress.Parse(strAddress);

        //Convert the prefix length to a valid SubnetMask

        int iMaskLength = 32;

        if(ipAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
        {
            iMaskLength = 128;
        }

        BitArray btArray = new BitArray(iMaskLength);
        for (int iC1 = 0; iC1 < iMaskLength; iC1++)
        {
            //Index calculation is a bit strange, since you have to make your mind about byte order.
            int iIndex = (int)((iMaskLength - iC1 - 1) / 8) * 8 + (iC1 % 8);

            if (iC1 < (iMaskLength - iPrefix))
            {
                btArray.Set(iIndex, false);
            }
            else
            {
                btArray.Set(iIndex, true);
            }
        }

        byte[] bMaskData = new byte[iMaskLength / 8];

        btArray.CopyTo(bMaskData, 0);

        //Create subnetmask
        Subnetmask smMask = new Subnetmask(bMaskData);

        //Get the IP range
        IPAddress ipaStart = IPAddressAnalysis.GetClasslessNetworkAddress(ipAddress, smMask);
        IPAddress ipaEnd = IPAddressAnalysis.GetClasslessBroadcastAddress(ipAddress, smMask);

        //Omit the following lines if your network range is large
        IPAddress[] ipaRange = IPAddressAnalysis.GetIPRange(ipaStart, ipaEnd);

        //Debug output
        foreach (IPAddress ipa in ipaRange)
        {
            Console.WriteLine(ipa.ToString());
        }

        Console.ReadLine();

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

Редактировать : обновлена ​​часть кода с изгибом битов.Может быть некрасиво, но работает на этом примере.Я думаю, что вы сможете найти лучшее решение, если вам нужно.Эти BitArrays - боль в шее.

Имейте в виду, что создание диапазона сети IPv6 может быть очень трудоемким занятием памяти / процессора, если сеть большая.

1 голос
/ 11 августа 2015

Я бы порекомендовал использовать библиотеку IPNetwork https://github.com/lduchosal/ipnetwork. Начиная с версии 2 он также поддерживает IPv4 и IPv6.

IPv6

  IPNetwork ipnetwork = IPNetwork.Parse("2001:0db8::/64");

  Console.WriteLine("Network : {0}", ipnetwork.Network);
  Console.WriteLine("Netmask : {0}", ipnetwork.Netmask);
  Console.WriteLine("Broadcast : {0}", ipnetwork.Broadcast);
  Console.WriteLine("FirstUsable : {0}", ipnetwork.FirstUsable);
  Console.WriteLine("LastUsable : {0}", ipnetwork.LastUsable);
  Console.WriteLine("Usable : {0}", ipnetwork.Usable);
  Console.WriteLine("Cidr : {0}", ipnetwork.Cidr);

выход

Network : 2001:db8::
Netmask : ffff:ffff:ffff:ffff::
Broadcast : 
FirstUsable : 2001:db8::
LastUsable : 2001:db8::ffff:ffff:ffff:ffff
Usable : 18446744073709551616
Cidr : 64

* Перечисление 1014 *

  IPNetwork network = IPNetwork.Parse("::/124");
  IPNetworkCollection ips = IPNetwork.Subnet(network, 128);

  foreach (IPNetwork ip in ips) {
      Console.WriteLine("{0}", ip);
  }

Выход

::/128
::1/128
::2/128
::3/128
::4/128
::5/128
::6/128
::7/128
::8/128
::9/128
::a/128
::b/128
::c/128
::d/128
::e/128
::f/128

Веселись!

1 голос
/ 01 февраля 2015

exNetworkLibrary - отличный инструмент, но если вы не можете использовать его в своем проекте, вы можете просто захотеть посмотреть эту статью:

http://www.codeproject.com/Articles/112020/IP-Address-Extension

В нем описывается, как маски адресов рассчитываются для использования в IPv4.

Ваш вопрос связан с IPv6, который я вижу, и, поскольку в .Net 4.5 существует метод IPAddress.MapToIPv6.

https://msdn.microsoft.com/en-us/library/system.net.ipaddress.maptoipv6(v=vs.110).aspx

Вы можете использовать это с проверками в статье для создания этого кода:

    private static IPAddress empty = IPAddress.Parse("0.0.0.0");
    private static IPAddress intranetMask1 = IPAddress.Parse("10.255.255.255");
    private static IPAddress intranetMask2 = IPAddress.Parse("172.16.0.0");
    private static IPAddress intranetMask3 = IPAddress.Parse("172.31.255.255");
    private static IPAddress intranetMask4 = IPAddress.Parse("192.168.255.255");

    /// <summary>
    /// Retuns true if the ip address is one of the following
    /// IANA-reserved private IPv4 network ranges (from http://en.wikipedia.org/wiki/IP_address)
    ///  Start        End   
    ///  10.0.0.0       10.255.255.255  
    ///  172.16.0.0       172.31.255.255    
    ///  192.168.0.0   192.168.255.255 
    /// </summary>
    /// <returns></returns>
    public static bool IsOnIntranet(this IPAddress ipAddress)
    {
        if (empty.Equals(ipAddress))
        {
            return false;
        }

        bool onIntranet = IPAddress.IsLoopback(ipAddress);

        if (false == onIntranet)
        {
            //Handle IPv6 by getting the IPv4 Mapped Address. 
            if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
            {
                onIntranet = ipAddress.Equals(ipAddress.And(intranetMask1.MapToIPv6())); //10.255.255.255
                onIntranet = onIntranet || ipAddress.Equals(ipAddress.And(intranetMask4.MapToIPv6())); ////192.168.255.255

                onIntranet = onIntranet || (intranetMask2.Equals(ipAddress.And(intranetMask2.MapToIPv6()))
                  && ipAddress.Equals(ipAddress.And(intranetMask3.MapToIPv6())));
            }
            else
            {
                onIntranet = ipAddress.Equals(ipAddress.And(intranetMask1)); //10.255.255.255
                onIntranet = onIntranet || ipAddress.Equals(ipAddress.And(intranetMask4)); ////192.168.255.255

                onIntranet = onIntranet || (intranetMask2.Equals(ipAddress.And(intranetMask2))
                  && ipAddress.Equals(ipAddress.And(intranetMask3)));
            }


        }

        return onIntranet;
    }

private static void CheckIPVersion(IPAddress ipAddress, IPAddress mask, out byte[] addressBytes, out byte[] maskBytes)
    {
        if (mask == null)
        {
            throw new ArgumentException();
        }

        addressBytes = ipAddress.GetAddressBytes();
        maskBytes = mask.GetAddressBytes();

        if (addressBytes.Length != maskBytes.Length)
        {
            throw new ArgumentException("The address and mask don't use the same IP standard");
        }
    }

    public static IPAddress And(this IPAddress ipAddress, IPAddress mask)
    {
        byte[] addressBytes;
        byte[] maskBytes;
        CheckIPVersion(ipAddress, mask, out addressBytes, out maskBytes);

        byte[] resultBytes = new byte[addressBytes.Length];
        for (int i = 0, e = addressBytes.Length; i < e; ++i)
        {
            resultBytes[i] = (byte)(addressBytes[i] & maskBytes[i]);
        }

        return new IPAddress(resultBytes);
    }
0 голосов
/ 25 октября 2016

Я знаю, что этому посту 5 лет, но, учитывая возможности Google, он также может быть обновлен сегодня утром.Итак, я добавлю немного пояснения с точки зрения сетевой инженерии.

Это зависит от того, какие адреса.Если вы имеете в виду каждый адрес в диапазоне, то приведенное выше обсуждение является правильным.Если вы имеете в виду адреса, которые могут быть уникально назначены узлу в подсети («одноадресные» адреса), имейте в виду, что в IPv6 (a) нет широковещательной передачи и (b) существует значительный диапазон многоадресной рассылки.

В основном: [подсеть]: ff :: зарезервировано для многоадресной рассылки.Если вы не используете / 64 для маски подсети, вы ДЕЙСТВИТЕЛЬНО хотите быть осторожными, потому что это противоречит фундаментальному предположению о многих RFC, связанных с IPv6.Есть другие RFC, которые предостерегают от использования адреса хоста со всеми нулями (но я не знаю конкретного требования на этот счет).

Итак, для подсети / 64,это означает, что диапазон адресов одноадресной рассылки: от: 0: 0: 0: 1 до :: feff: ffff: ffff: ffff.

См. здесь для обсуждения: http://www.tcpipguide.com/free/t_IPv6MulticastandAnycastAddressing.htm

weylin

...