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:
- 10.0.0.0/24
- 172.16.0.0/20
- 192.168.0.0/16
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 . . .