Checking for Private IPs in .NET

Posted by Tom on 2011-02-15 11:11

We've got a client who is doing IP-based geolocation when a user first hits to the site via a third-party service. They've got a very limited number of lookups they can make per day, and it turns out the hosting company's heartbeat service is eating up a fair amount of these. What we wanted is to filter out calls made from private IP addresses. I was expecting to come across something in .NET to do this, but I'm damned if I can find it.

According to RFC 1918 we have 3 blocks of IPs in the private address space:

Since the easiest way to perform the comparison is going to be using the IP in integer format, first we need a method for converting an IP from the more widely used dotted octet. I'm pretty much rewriting the System.Net.IPAddress.Address property, which has been deprecated since the dawn of time.

using System;
 
public static class IpUtils
{
    private static readonly long[] _privateBlocks;
 
    static IpUtils()
    {
        _privateBlocks = new long[]
        { 
            IpToInteger("10.0.0.0"), 
            IpToInteger("172.16.0.0"), 
            IpToInteger("192.168.0.0") 
        };
    }
 
    public static long IpToInteger(string ip)
    {
        long result = 0;
        string [] octets = ip.ToString().Split(new char [] { '.' });
        result = Convert.ToInt64(
                  Int32.Parse(octets[0]) * Math.Pow(2, 24)
                + Int32.Parse(octets[1]) * Math.Pow(2, 16)
                + Int32.Parse(octets[2]) * Math.Pow(2, 8)
                + Int32.Parse(octets[3]) * Math.Pow(2, 0));
        return result;
    }
 
    public static bool IsPrivateIp(string ip)
    {
        long ipInt = IpToInteger(ip);
        return (ipInt >= _privateBlocks[0] && ipInt < _privateBlocks[0] + Math.Pow(2, 24))
            || (ipInt >= _privateBlocks[1] && ipInt < _privateBlocks[1] + Math.Pow(2, 20))
            || (ipInt >= _privateBlocks[2] && ipInt < _privateBlocks[2] + Math.Pow(2, 16));
    }
}

This is coded for the sake of readability and is very inefficient. All of the Math.Pow calls are constants (does the compiler work this out? Or the JIT? Answers on a postcard) and we could swap out all of that _privateBlocks stuff in favour of 167772160, 2886729728 and 3232235520 if you want. And using bit-shifts rather than multiplication would probably save you a few micro-seconds . . .