Wednesday 1 April 2015

Filtering and identifying IP addresses uniquely

While developing cloud services or web applications, one might often want to filter out requests from specific IP addresses like 127.0.0.1 (localhost), 192.168.0.0 (private network), etc. While blocking these IP addresses at the firewall level is an obvious choice, sometimes one might want to handle them at the application layer.

This article discusses an optimal solution to identify such IP addresses. These IP addresses belong to a range of Reserved IP addresses.  I am going to assume that you want to identify some of the address blocks on this wiki.

A naive approach is to examine the prefix of the IP address string in the HTTP header by extracting the IP address and checking if it has a prefix that corresponds to a reserved CIDR. Example: Suppose that you want to filter all the request hitting your servers from 127.0.0.0/8 and 10.0.0.0/8. This can simply be done by checking if the incoming HTTP request is originating from an IP address string which starts with "127." or "10." .

While that would work, this approach becomes a problem when you want filter 172.16.0.0/12 as the number of string comparisons would drastically increase in this case (16 prefixes ranging from "172.16." to "172.31." )

Well, here's an approach to avoid this problem:

The IPv4 address space ranges from 0.0.0.0 to 255.255.255.255 or in other words, it can be treated as a number with base 256 after eliminating the '.' delimiters. When treated as a number, the problem simplifies greatly as the only thing to do now is to check whether the given number in base 256 (the IP address) falls within a set of ranges (or reserved IP address blocks) which are in base 256 as well.

I going to assume your programming language of choice and you are accustomed to the decimal number system (base 10), so I am taking the approach of converting the IP address and the reserved IP address blocks in base 256 to their corresponding representation in base 10.

Example:
Assume the IP address to be examined is a.b.c.d. We want to check if it belongs to 127.0.0.0/8, 10.0.0./8 or 172.0.0.0/12.

Step 1 - Convert the incoming IP address to base 10.
  • Base 10 representation of a.b.c.d is:
    a*256^3 + b*256^2 + c*256^1 + d*256^0
  • Let this be equal to x.
    x = a*256^3 + b*256^2 + c*256^1 + d*256^0

Step 2 - Check if x (base 10) falls within the range of the IP address blocks' corresponding base 10 representation
  • 127.0.0.0 - 127.255.255.255 (127.0.0.0/8) -> 2130706432 - 2147483647

    if x >= 2130706432 && x <= 2147483647?
  • 10.0.0.0 - 10.255.255.255 (10.0.0./8) -> 167772160 - 184549375

    if x >= 167772160 && x <= 184549375
  • 172.16.0.0 - 172.31.255.255 (172.16.0.0/12) ->   2886729728 - 2887778303

    if x >= 2886729728 && x <= 2887778303
Also, this base conversion approach can be used to represent a IP address uniquely using a decimal number. If you feel the numbers are too big to be easily managed in your system, applying the logarithmic function on x could be useful i.e, using log(x) as the IP's unique identifier instead of x.