2011-08-16 45 views
6

我见过很多很棒的C# examples,它演示了如何将CIDR表示法(例如192.168.0.1/25)中提供的IPv4地址转换为它们的相关范围(192.168.0.1 - 192.168 .0.126)。我的程序需要能够做到这一点(计算我的本地子网内的所有地址),但我也想支持IPv6。计算一个子网内的所有地址...对于IPv6

如果我的C#程序具有我所有典型的ipconfig信息(IPv4地址,子网掩码,IPv6地址,本地链接v6地址,默认网关) - 我将如何生成所有IPv6地址列表我的本地子网并将它们输出到控制台?

+2

你可能需要重新考虑你的功能。按照设计,几乎你会在IPv6上看到的任何子网都是/ 64,或(2^64)-1主机大。 – Joe

+2

这是正确的,我想要所有的18,446,744,073,709,551,616个IP地址;) – DaveUK

+8

你会怎么做?即使是每秒1000万,你也需要58000年时间才能完成。 – Joe

回答

9

您可以使用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的任务。

+0

这几乎为我工作。需要将iIndex更改为相等(iMaskLength - iC1 - 1)。这是编辑前的代码,所以我不确定它为什么被改变。 –

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

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

玩得开心!

+0

第一个和最后一个可用地址的输出不正确。与IPv4不同,IPv6可以使用子网中的所有地址。一个标准的IPv6子网是/ 64(一些特殊情况下使用其他掩码长度),可用地址从' ::'到':ffff:ffff:ffff:ffff'。子网没有预留(' ::'),并且IPv6没有广播的概念,因此在':ffff:ffff:ffff:ffff'处不存在广播地址。 –

+1

感谢您指出这一点。示例已更正。 – LukeSkywalker

0

我知道这篇文章是5年前的,但考虑到Google的功能,它可能已经在今天早上更新了。所以,我会从网络工程的角度加入一些澄清。

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

基本上:[subnet]:ff ::保留给组播。如果你没有使用/ 64作为子网掩码,你真的要小心,因为它违背了一个基本的假设,就是很多IPv6相关的RFC。还有其他一些RFC告诫不要使用全零主机地址(但我不知道具体的要求)。

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

看到这里的讨论: http://www.tcpipguide.com/free/t_IPv6MulticastandAnycastAddressing.htm

weylin

+0

全零位主机地址是为子网路由器保留的,它必须在这个地址上作出响应。参见[RFC 4291第2.6.1节](https://tools.ietf.org/html/rfc4291#section-2.6.1)。尽管有这种要求,但许多网络上的路由器都不会对此地址做出响应,但如果您尝试在路由器正常工作的网络上使用此地址,那么您将会遇到糟糕的一天。 –