我见过很多很棒的C# 示例,它们演示了如何将 CIDR 表示法中提供的 IPv4 地址(例如 192.168.0.1/25)转换为其相关范围(192.168.0.1 - 192.168.0.126)。我的程序需要能够做到这一点(计算我本地子网中的所有地址),但我还想支持 IPv6。
如果我的 C# 程序具有我所有的典型 ipconfig 信息(IPv4 地址、子网掩码、IPv6 地址、链接本地 v6 地址、默认网关) - 我将如何生成本地子网中所有 IPv6 地址的列表和将它们输出到控制台?
您可以使用 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();
我不完全确定我是否已经完成了从前缀长度到包含子网掩码的字节数组的转换,但是这段代码应该给你一个很好的起点。
编辑:更新了代码的位弯曲部分。可能很难看,但适用于这个例子。如果需要,我认为您将能够找到更好的解决方案。那些 BitArray 让人头疼。
请注意,如果网络很大,生成 IPv6 网络范围可能是一项非常耗费内存/cpu 的任务。
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);
}
我建议使用 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
玩得开心 !
我知道这篇文章已有 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
卫林