Search code examples
c#ipv6

How to determine if IPv6 address is private?


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:

  • make sure the first word is at least 4 characters long as suggested by @RonMaupin
  • improved comment above 'else return false' as suggested by @RonMaupin
  • check for 'fe80' prefix as suggested by @KevinBurdett
  • check for 'Discard' prefix as suggested by @KevinBurdett

Solution

  • 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;
    }