CIDR(Classless Inter-Domain Routing)是“无类别域间路由”的缩写。是当前用来表示路由的一种方式。由于在做某产品的时候需要用到CIDR集合的一些算法,例如集合的交集、并集、补集运算。下面给出相应的实现代码。
首先是CIDR的结构:
public sealed class CIDR : IXmlSerializable, IComparable<CIDR>
{
private byte[] bytes = null;
public IPAddress IPAddress { get; private set; }
public int Subnet { get; private set; }
public override bool Equals(object obj)
{
CIDR cidr = obj as CIDR;
if (cidr == null)
return false;
return this.Subnet == cidr.Subnet && this.IPAddress.Equals(cidr.IPAddress);
}
public override int GetHashCode()
{
return this.IPAddress.GetHashCode() ^ this.Subnet.GetHashCode();
}
public override string ToString()
{
return string.Format("{0}/{1}", IPAddress, Subnet);
}
public static CIDR Parse(string str)
{
CIDR cidr = new CIDR();
cidr.SetFromString(str);
return cidr;
}
private void SetFromString(string str)
{
int i = str.IndexOf('/');
this.IPAddress = IPAddress.Parse(str.Substring(0, i));
this.Subnet = int.Parse(str.Substring(i + 1));
this.bytes = this.IPAddress.GetAddressBytes();
int length = this.bytes.Length << 3;
if (this.Subnet < 0 || this.Subnet > length)
throw new ArgumentException("", "str");
// can optimize
for (i = this.Subnet + 1; i <= length; i++)
{
int bit = (bytes[(i - 1) >> 3] >> ((8 - i) & 7)) & 1;
if (bit != 0)
throw new ArgumentException("", "str");
}
}
public bool IsIPv6
{
get { return this.IPAddress.AddressFamily == AddressFamily.InterNetworkV6; }
}
#region IXmlSerializable
XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
void IXmlSerializable.ReadXml(XmlReader reader)
{
this.SetFromString(reader.ReadElementContentAsString());
}
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteString(this.ToString());
}
#endregion
#region CIDR Algorithm
public CIDR GetPaired()
{
if (this.Subnet == 0)
throw new NotSupportedException("Subnet=0");
CIDR cidr = new CIDR();
cidr.Subnet = this.Subnet;
cidr.bytes = new byte[this.bytes.Length];
this.bytes.CopyTo(cidr.bytes, 0);
int k = this.Subnet - 1;
int i = k >> 3;
int j = (7 - k) & 7;
cidr.bytes[i] = (byte)(cidr.bytes[i] ^ (1 << j));
cidr.IPAddress = new IPAddress(cidr.bytes);
return cidr;
}
public CIDR GetBiggerSubnet()
{
if (this.Subnet == 0)
throw new NotSupportedException("Subnet=0");
CIDR cidr = new CIDR();
cidr.Subnet = this.Subnet - 1;
cidr.bytes = new byte[this.bytes.Length];
this.bytes.CopyTo(cidr.bytes, 0);
int k = this.Subnet - 1;
int i = k >> 3;
int j = (7 - k) & 7;
cidr.bytes[i] = (byte)(cidr.bytes[i] & ~(1 << j));
cidr.IPAddress = new IPAddress(cidr.bytes);
return cidr;
}
public CIDR GetSmallerSubnet()
{
if (this.Subnet == this.bytes.Length << 3)
throw new NotSupportedException("Subnet=max");
CIDR cidr = new CIDR();
cidr.Subnet = this.Subnet + 1;
cidr.bytes = this.bytes;
cidr.IPAddress = this.IPAddress;
return cidr;
}
public bool ExistsIntersection(CIDR other)
{
if (other == null)
throw new ArgumentNullException("other");
if (this.IsIPv6 != other.IsIPv6)
throw new NotSupportedException("Different address family");
if (this.Subnet == 0 || other.Subnet == 0)
return true;
int min = Math.Min(this.Subnet, other.Subnet) - 1;
int length = min >> 3;
int k;
for (k = 0; k < length; k++)
if (this.bytes[k] != other.bytes[k])
return false;
//int m = (0xff << (7 - min & 7)) & 0xff;
int m = (0xff80 >> (min & 7));
if (k < this.bytes.Length && (this.bytes[k] & m) != (other.bytes[k] & m))
return false;
return true;
}
public CIDR Intersect(CIDR other)
{
if (other == null)
throw new ArgumentNullException("other");
if (this.IsIPv6 != other.IsIPv6)
throw new NotSupportedException("Different address family");
if (this.ExistsIntersection(other))
if (this.Subnet > other.Subnet)
return this;
else
return other;
else
return null;
}
public CIDR[] Union(CIDR other)
{
if (other == null)
throw new ArgumentNullException("other");
if (this.IsIPv6 != other.IsIPv6)
throw new NotSupportedException("Different address family");
if (this.ExistsIntersection(other))
if (this.Subnet < other.Subnet)
return new[] { this };
else
return new[] { other };
else
if (this.Subnet == other.Subnet && this.Equals(other.GetPaired()))
return new[] { this.GetBiggerSubnet() };
else
return new[] { this, other };
}
#endregion
public int CompareTo(CIDR other)
{
if (other == null)
throw new ArgumentNullException("other");
if (this.IsIPv6 != other.IsIPv6)
if (this.IsIPv6)
return 1;
else
return -1;
else if (this.Subnet > other.Subnet)
return 1;
else if (this.Subnet < other.Subnet)
return -1;
else
for (int i = 0; i < this.bytes.Length; i++)
if (this.bytes[i] > other.bytes[i])
return 1;
else if (this.bytes[i] < other.bytes[i])
return -1;
return 0;
}
}
这个类实现了序列化接口(IXmlSerializable)和可比较接口(IComparable<T>)。方便进行存储等操作。
接下来是对CIDR集合的操作。由于在产品中叫做RouteGroup,所以在这里也就没改名字了:
public class RouteGroup
{
public RouteGroup()
{
this.CIDRs = new HashSet<CIDR>();
}
[XmlAttribute]
public string GroupName { get; set; }
[XmlAttribute]
public Guid GroupID { get; set; }
public HashSet<CIDR> CIDRs { get; set; }
public static RouteGroup Create(string groupName)
{
return new RouteGroup { GroupName = groupName, GroupID = Guid.NewGuid() };
}
public void Union(CIDR cidr)
{
if (cidr == null)
throw new ArgumentNullException("cidr");
var list = this.CIDRs
.Where(s => s.IsIPv6 == cidr.IsIPv6 && s.ExistsIntersection(cidr))
.ToList();
if (list.Count == 0)
{
if (cidr.Subnet > 0 && this.CIDRs.Remove(cidr.GetPaired()))
this.Union(cidr.GetBiggerSubnet());
else
this.CIDRs.Add(cidr);
}
else
{
foreach (var item in list)
this.CIDRs.Remove(item);
var max = list.Max();
if (max.Subnet < cidr.Subnet)
this.CIDRs.Add(max);
else if (max.Subnet > cidr.Subnet)
this.CIDRs.Add(cidr);
}
}
public void Subtract(CIDR cidr)
{
if (cidr == null)
throw new ArgumentNullException("cidr");
var list = this.CIDRs
.Where(s => s.IsIPv6 == cidr.IsIPv6 && s.ExistsIntersection(cidr))
.ToList();
if (list.Count > 0)
{
foreach (var item in list)
this.CIDRs.Remove(item);
var min = list.Min();
CIDR other = cidr;
for (int i = cidr.Subnet; i > min.Subnet; i--)
{
this.CIDRs.Add(other.GetPaired());
other = other.GetBiggerSubnet();
}
}
}
public void Union(string cidr)
{
this.Union(CIDR.Parse(cidr));
}
public void Subtract(string cidr)
{
this.Subtract(CIDR.Parse(cidr));
}
public void Reverse()
{
var list = this.CIDRs.ToList();
this.CIDRs.Clear();
this.Union("0.0.0.0/0");
foreach (var item in list)
this.Subtract(item);
}
public void Sort()
{
var list = this.CIDRs.OrderBy(s => s).ToList();
this.CIDRs.Clear();
foreach (var item in list)
{
this.CIDRs.Add(item);
}
}
public void Rebuild()
{
var list = this.CIDRs.OrderBy(s => s).ToList();
this.CIDRs.Clear();
foreach (var item in list)
{
this.Union(item);
}
}
}
样例代码:
static void Main(string[] args)
{
var rg = RouteGroup.Create("test");
rg.Union("1.2.0.0/16");
rg.Union("1.3.0.0/16");
rg.Union("2.2.0.0/16");
rg.Union("3.2.0.0/16");
rg.Union("4.2.5.0/24");
rg.Union("5.2.8.0/21");
rg.Union("4.2.12.0/22");
rg.Sort();
foreach (var item in rg.CIDRs)
Console.WriteLine(item);
Console.WriteLine();
rg.Reverse();
rg.Sort();
foreach (var item in rg.CIDRs)
Console.WriteLine(item);
Console.WriteLine();
rg.Subtract("0.0.0.0/2");
rg.Sort();
foreach (var item in rg.CIDRs)
Console.WriteLine(item);
/*
* 1.2.0.0/15
* 2.2.0.0/16
* 3.2.0.0/16
* 5.2.8.0/21
* 4.2.12.0/22
* 4.2.5.0/24
*
* 128.0.0.0/1
* 64.0.0.0/2
* 32.0.0.0/3
* 16.0.0.0/4
* 8.0.0.0/5
* 6.0.0.0/7
* 0.0.0.0/8
* 1.128.0.0/9
* 2.128.0.0/9
* 3.128.0.0/9
* 4.128.0.0/9
* 5.128.0.0/9
* 1.64.0.0/10
* 2.64.0.0/10
* 3.64.0.0/10
* 4.64.0.0/10
* 5.64.0.0/10
* 1.32.0.0/11
* 2.32.0.0/11
* 3.32.0.0/11
* 4.32.0.0/11
* 5.32.0.0/11
* 1.16.0.0/12
* 2.16.0.0/12
* 3.16.0.0/12
* 4.16.0.0/12
* 5.16.0.0/12
* 1.8.0.0/13
* 2.8.0.0/13
* 3.8.0.0/13
* 4.8.0.0/13
* 5.8.0.0/13
* 1.4.0.0/14
* 2.4.0.0/14
* 3.4.0.0/14
* 4.4.0.0/14
* 5.4.0.0/14
* 1.0.0.0/15
* 2.0.0.0/15
* 3.0.0.0/15
* 4.0.0.0/15
* 5.0.0.0/15
* 2.3.0.0/16
* 3.3.0.0/16
* 4.3.0.0/16
* 5.3.0.0/16
* 4.2.128.0/17
* 5.2.128.0/17
* 4.2.64.0/18
* 5.2.64.0/18
* 4.2.32.0/19
* 5.2.32.0/19
* 4.2.16.0/20
* 5.2.16.0/20
* 5.2.0.0/21
* 4.2.0.0/22
* 4.2.8.0/22
* 4.2.6.0/23
* 4.2.4.0/24
*
* 128.0.0.0/1
* 64.0.0.0/2
* Press any key to continue . . .
*/
}
欢迎拍砖讨论。