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.
1: using System;
2:
3: public static class IpUtils
4: {
5: private static readonly long[] _privateBlocks;
6:
7: static IpUtils()
8: {
9: _privateBlocks = new long[]
10: {
11: IpToInteger("10.0.0.0"),
12: IpToInteger("172.16.0.0"),
13: IpToInteger("192.168.0.0")
14: };
15: }
16:
17: public static long IpToInteger(string ip)
18: {
19: long result = 0;
20: string [] octets = ip.ToString().Split(new char [] { '.' });
21: result = Convert.ToInt64(
22: Int32.Parse(octets[0]) * Math.Pow(2, 24)
23: + Int32.Parse(octets[1]) * Math.Pow(2, 16)
24: + Int32.Parse(octets[2]) * Math.Pow(2, 8)
25: + Int32.Parse(octets[3]) * Math.Pow(2, 0));
26: return result;
27: }
28:
29: public static bool IsPrivateIp(string ip)
30: {
31: long ipInt = IpToInteger(ip);
32: return (ipInt >= _privateBlocks[0] && ipInt < _privateBlocks[0] + Math.Pow(2, 24))
33: || (ipInt >= _privateBlocks[1] && ipInt < _privateBlocks[1] + Math.Pow(2, 20))
34: || (ipInt >= _privateBlocks[2] && ipInt < _privateBlocks[2] + Math.Pow(2, 16));
35: }
36: }
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 . . .