在IPv6网络中计算子网内的所有地址…

9
我看到了许多很棒的C#示例,演示了如何将以CIDR表示法(例如192.168.0.1/25)提供的IPv4地址转换为它们相关的范围(192.168.0.1 - 192.168.0.126)。我的程序需要能够执行此操作(计算本地子网中的所有地址),但我还想支持IPv6。
如果我的C#程序具有所有典型的ipconfig信息(IPv4地址、子网掩码、IPv6地址、链路本地v6地址、默认网关)- 我该如何生成本地子网中所有IPv6地址的列表并将其输出到控制台?

2
你可能需要重新思考你的功能。按设计,几乎在IPv6中看到的任何子网都将是/64,或者说有(2^64)-1个主机。 - Joe
3
没错,我想要所有的18,446,744,073,709,551,616个IP地址 ;) - DaveUK
11
你将如何处理它们?即使每秒钟处理1000万个,也需要58000年才能全部处理完。 - Joe
1
@Joe - 我认为他只是想将 2001:DB8::/48 转换为 2001:DB8:0:0:0:0:0:0 - 2001:DB8:0:FFFF:FFFF:FFFF:FFFF:FFFF,而不是获取每个可能的地址。 - CodeNaked
2
谢谢大家。Emiswelt已发布了代码,实际上提供了这两个东西...它将给我范围或(如果我想自杀)单个IP地址。 - DaveUK
显示剩余3条评论
4个回答

10

您可以使用eExNetworkLibrary中的eExNetworkLibrary.IP.IPAddressAnalysis类。

以下代码适用于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网络范围可能是一项非常耗费内存/ CPU资源的任务。


这个对我来说几乎可行。需要将iIndex更改为等于(iMaskLength-iC1-1)。这是编辑之前的代码,所以我不确定为什么要更改它。 - Brian S

2
我建议使用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

枚举

  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

玩得开心!


第一个和最后一个可用地址的输出是不正确的。与IPv4不同,IPv6可以使用子网中的所有地址。标准的IPv6子网是/64(一些特殊情况使用其他掩码长度),可用地址范围从<子网>::<子网>:ffff:ffff:ffff:ffff。子网没有保留地址(<子网>::),而且IPv6没有广播的概念,因此在<子网>:ffff:ffff:ffff:ffff不存在广播地址。 - Ron Maupin

1

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

我知道这篇文章已经五年了,但考虑到谷歌的能力,它可能早上就被更新了。所以,我会从网络工程的角度添加一些澄清。

这取决于什么样的地址。如果你指的是范围内的每个地址,那么上面的讨论是正确的。如果你指的是可以唯一分配给子网中的节点的地址(“单播”地址),请注意,在IPv6中(a)没有广播,(b)有一个相当大的多播范围。

基本上:[子网]:ff:: 保留用于多播。如果你不使用/64作为子网掩码,你真的要小心,因为它违反了许多与IPv6相关的RFC的基本假设。还有其他的RFC警告不要使用全零主机地址(但我不知道是否有具体的要求)。

因此,对于/64子网,这意味着单播地址的范围是::0:0:0:1到::feff:ffff:ffff:ffff。

请参见此处的讨论: http://www.tcpipguide.com/free/t_IPv6MulticastandAnycastAddressing.htm

weylin


全零主机地址保留给子网路由器,必须在此地址上做出响应。详见RFC 4291第2.6.1节。尽管有此要求,但许多网络上的路由器不会对此地址做出响应,但如果您尝试在路由器正常工作的网络上使用此地址,您将会度过糟糕的一天。 - Michael Hampton
最近我和一位思科IPv6开发人员进行了讨论。简而言之,全零地址“应该可以工作”。然而,(a)与路由器的交互几乎总是使用链路本地地址(FE80::/10),(b)如果您在具有冗余路由器的子网中,HSRP、VRRP和GLBP的性能要好得多,(c)针对您的观点,这个“任意路由器”任播地址在各种操作系统上的支持并不像您认为的那样良好,并且许多使用该地址的情况下使用不正确。 - Weylin Piegorsch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接