Reputation: 3715
I am trying to determine if a given IPv6 address is private or not in C# and I was tempted to simply use the 'IsIPv6SiteLocal' property on the IPAddress class. However, as explained in this comment, the logic implemented in this property is deprecated. I ran the following unit test:
[TestMethod]
public void IsPrivate_ipv6_True()
{
// This sample private IPv6 address was generated using: http://unique-local-ipv6.com/
var ip = IPAddress.Parse("fd44:fda4:e1ba::1");
Assert.IsTrue(ip.IsIPv6SiteLocal);
}
The assertion in the unit test fails which confirms that IsIPv6SiteLocal does not correctly determines if an address is local. So I need an alternative.
I wrote the following extension method and I was wondering if anybody can think of a scenario where it would not properly determine if the address is private/public.
public static bool IsPrivateIPv6(this IPAddress address)
{
var addressAsString = address.ToString();
var firstWord = addressAsString.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries)[0];
// Make sure we are dealing with an IPv6 address
if (address.AddressFamily != AddressFamily.InterNetworkV6) return false;
// The original IPv6 Site Local addresses (fec0::/10) are deprecated. Unfortunately IsIPv6SiteLocal only checks for the original deprecated version:
else if (address.IsIPv6SiteLocal) return true;
// These days Unique Local Addresses (ULA) are used in place of Site Local.
// ULA has two variants:
// fc00::/8 is not defined yet, but might be used in the future for internal-use addresses that are registered in a central place (ULA Central).
// fd00::/8 is in use and does not have to registered anywhere.
else if (firstWord.Substring(0, 2) == "fc" && firstWord.Length >= 4) return true;
else if (firstWord.Substring(0, 2) == "fd" && firstWord.Length >= 4) return true;
// Link local addresses (prefixed with fe80) are not routable
else if (firstWord == "fe80") return true;
// Discard Prefix
else if (firstWord == "100") return true;
// Any other IP address is not Unique Local Address (ULA)
else return false;
}
EDITED 2/13/2016:
Upvotes: 6
Views: 6983
Reputation: 71
Some of the answers provided here include a check for "::1" which is the IPv6 address for localhost.
Technically localhost is not a private ip address and should be checked for separately.
For a check including IPv4 and IPv6 addresses and optionally localhost you can port this javascript function.
This should be a comment but my reputation is not high enough yet.
Upvotes: 0
Reputation: 17929
Improved @desautelsj's answer by adding a special case for ::1
and avoiding an ArgumentException from his solution (that would happen in the Substring()
call):
public static bool IsPrivateIPv6(IPAddress address)
{
// Make sure we are dealing with an IPv6 address
if (address.AddressFamily != AddressFamily.InterNetworkV6)
throw new ArgumentException("IP address is not V6", "address");
var addressAsString = address.ToString();
var firstWord = addressAsString.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries)[0];
// equivalent of 127.0.0.1 in IPv6
if (addressAsString == "::1")
return true;
// The original IPv6 Site Local addresses (fec0::/10) are deprecated. Unfortunately IsIPv6SiteLocal only checks for the original deprecated version:
else if (address.IsIPv6SiteLocal)
return true;
// These days Unique Local Addresses (ULA) are used in place of Site Local.
// ULA has two variants:
// fc00::/8 is not defined yet, but might be used in the future for internal-use addresses that are registered in a central place (ULA Central).
// fd00::/8 is in use and does not have to registered anywhere.
else if (firstWord.Length >= 4 && firstWord.Substring(0, 2) == "fc")
return true;
else if (firstWord.Length >= 4 && firstWord.Substring(0, 2) == "fd")
return true;
// Link local addresses (prefixed with fe80) are not routable
else if (firstWord == "fe80")
return true;
// Discard Prefix
else if (firstWord == "100")
return true;
// Any other IP address is not Unique Local Address (ULA)
return false;
}
And in F#:
let private IsIpv6AddressPrivate (address: IPAddress) =
if address.AddressFamily = AddressFamily.InterNetwork then
invalidArg "address" "address must be IPv6"
// The original IPv6 Site Local addresses (fec0::/10) are deprecated. Unfortunately IsIPv6SiteLocal only checks for the original deprecated version:
elif address.IsIPv6SiteLocal then
true
else
let addressAsString = address.ToString()
// equivalent of 127.0.0.1 in IPv6
if addressAsString = "::1" then
true
else
let firstWord = addressAsString.Split([|':'|], StringSplitOptions.RemoveEmptyEntries).[0]
// These days Unique Local Addresses (ULA) are used in place of Site Local.
// ULA has two variants:
// fc00::/8 is not defined yet, but might be used in the future for internal-use addresses that are registered in a central place (ULA Central).
// fd00::/8 is in use and does not have to registered anywhere.
if (firstWord.Length >= 4 && firstWord.Substring(0, 2) = "fc") ||
(firstWord.Length >= 4 && firstWord.Substring(0, 2) = "fd") ||
// Link local addresses (prefixed with fe80) are not routable
(firstWord = "fe80") ||
// Discard Prefix
(firstWord = "100") then
true
else
false
Upvotes: 3
Reputation: 3715
Here's the final code I used and so far it seems to be working as intended:
public static bool IsPrivateIPv6(this IPAddress address)
{
var addressAsString = address.ToString();
var firstWord = addressAsString.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries)[0];
// Make sure we are dealing with an IPv6 address
if (address.AddressFamily != AddressFamily.InterNetworkV6) return false;
// The original IPv6 Site Local addresses (fec0::/10) are deprecated. Unfortunately IsIPv6SiteLocal only checks for the original deprecated version:
else if (address.IsIPv6SiteLocal) return true;
// These days Unique Local Addresses (ULA) are used in place of Site Local.
// ULA has two variants:
// fc00::/8 is not defined yet, but might be used in the future for internal-use addresses that are registered in a central place (ULA Central).
// fd00::/8 is in use and does not have to registered anywhere.
else if (firstWord.Substring(0, 2) == "fc" && firstWord.Length >= 4) return true;
else if (firstWord.Substring(0, 2) == "fd" && firstWord.Length >= 4) return true;
// Link local addresses (prefixed with fe80) are not routable
else if (firstWord == "fe80") return true;
// Discard Prefix
else if (firstWord == "100") return true;
// Any other IP address is not Unique Local Address (ULA)
else return false;
}
Upvotes: 0
Reputation: 2982
While not particularly elegant... I can't really think of anything better :)
However, I would also check for link local addresses, prefixed with fe80:
. They are not routable, but if you are pulling the IP directly from an interface, it may still report the link local when no other IPv6 addresses are present.
The wikipedia article on IPv6 (https://en.wikipedia.org/wiki/Reserved_IP_addresses#IPv6) also shows 100:
as a discard prefix. Depending on what you are trying to accomplish, you may want to check for these too.
Upvotes: 1