Introduction
It is occasionally necessary to determine the relative position of two IP addresses or if a particular IP address falls within a particular range. In the 1.0 and 1.1 frameworks, the IPAddress
object had an 'Address
' property. This returned a long
value as a representation of an IP address, and allowed simple numeric comparison of IP addresses to determine their relative values.
In the 2.0 framework, the Address
property is deprecated. This may be due to the advent of support for IPv6 addressing. IPv4 addresses can be represented with 32 bits of data, and therefore can be represented as a long
. IPv6 addresses are made up of 128 bits of data, and this poses a problem if trying to be represented as a long
value.
In the 2.0 framework, the IPAddress
object has an Equal()
method to determine if two IPAddress
objects are the same, but there is no inbuilt method to determine the relative position of two addresses that are not equal. In looking for existing solutions available, the only ones that I could find used text comparisons to determine the relative values of two IP addresses. I've created a solution that doesn't use text comparisons, by using existing methods and extending the existing IPAddress
object.
Solution
The IPAddress
object can be inherited to extend the functionality. This article looks at a basic method to add functionality to the base object to determine the relative position of two addresses and to determine if an address is within a given range. These functions will support both IPv4 and IPv6.
Key Concept
The 2.0 framework IPAddress
object has a method called GetAddressBytes()
which returns a byte
array representation of the stored address. For IPv4, this byte
array has a length = 4; for IPv6, the array length = 16. Given two addresses, by comparing the relative value of each pair of bytes, it is trivial to determine the relative position of the two IP addresses.
Comparing two addresses from different address families is not supported. It is impossible to determine if an IPv4 address is higher or lower than an IPv6 address.
ComparibleIPAddress
The derived object will be called ComparibleIPAddress
. It should inherit from System.Net.IPAddress
.
Constructors
You must specify a constructor for the inherited object. All the constructor must do is call the constructor of the base object. This is necessary as, in this case, the base object doesn't have a parameter-less constructor.
For consistency, I have included a constructor to mirror each of the available constructors in the base object.
public ComparibleIPAddress(byte[] address)
: base(address)
{
}
public ComparibleIPAddress(Int64 address)
: base(address)
{
}
public ComparibleIPAddress(byte[] address, Int64 scopeid)
: base(address, scopeid)
{
}
CompareTo Function
The purpose of this function is to allow us to compare the current IP address to a given value and determine their relative positions. If the current IP address and the passed address are not in the same address family, the function throws an ArgumentOutOfRangeException
.
Each address is converted to a byte
array.
Each pair in the two byte
arrays are compared with each other. If they are not equal, their relative values are determined and processing is completed.
public int CompareTo(ComparibleIPAddress value)
{
int returnVal = 0;
if (this.AddressFamily == value.AddressFamily)
{
byte[] b1 = this.GetAddressBytes();
byte[] b2 = value.GetAddressBytes();
for (int i = 0; i < b1.Length; i++)
{
if (b1[i] < b2[i])
{
returnVal = -1;
break;
}
else if (b1[i] > b2[i])
{
returnVal = 1;
break;
}
}
}
else
{
throw new ArgumentOutOfRangeException("value",
"Cannot compare two addresses no in the same Address Family.");
}
return returnVal;
}
IsInRange Function
This is an extension of the CompareTo
function. Once we are able to determine the relative position of two addresses, it is trivial to determine if a given addresses is within a range of addresses.
This function checks that all addresses are within the same address families. Again, an ArgumentOutOfRangeException
is thrown if they are not.
public bool IsInRange(ComparibleIPAddress rangeStartAddress,
ComparibleIPAddress rangeEndAddress)
{
bool returnVal = false;
if (rangeStartAddress.AddressFamily != rangeEndAddress.AddressFamily)
{
throw new ArgumentOutOfRangeException("rangeStartAddress",
String.Format("The Start Range type {0} and End Range type {1}" +
" are not compatible ip address families.",
rangeStartAddress.AddressFamily.ToString(),
rangeEndAddress.AddressFamily.ToString()));
}
if (rangeStartAddress.AddressFamily == this.AddressFamily)
{
returnVal = (CompareTo(rangeStartAddress) >= 0
&& CompareTo(rangeEndAddress) <= 0);
}
else
{
throw new ArgumentOutOfRangeException("rangeStartAddress",
String.Format("The range type {0} and current value type {1}" +
" are not compatible ip address families",
rangeStartAddress.AddressFamily.ToString(),
this.AddressFamily.ToString()));
}
return returnVal;
}
Summary
A derived class can easily be created to provide additional functions for IP addresses.