views:

908

answers:

2

So I have ~12600 subnets:

eg. 123.123.208.0/20

and an IP.

I can use a SQLite Database or an array or whatever

There was a similar question asked about a month ago, however I am not looking for checking one IP against one subnet but a bunch of subnets (obviously the most efficient way, hopefully not O(total subnets)) :)

How can I check that the IP is one of in one of these subnets, I need true or false not the subnet if that helps optimisation.

There are similar subnets in the current list eg.: (actual extract)

123.123.48.0/22 <-- not a typo
123.123.48.0/24 <-- not a typo
123.123.90.0/24
123.123.91.0/24
123.123.217.0/24

In total they range from 4.x.y.z to 222.x.y.z

+2  A: 

Convert the lower ip and the upper ip in the range to integers and store the range in the db then make sure both columns are indexed.

Off the top of my head (pseudo code):

function ipmap(w,x,y,z) {
  return 16777216*w + 65536*x + 256*y + z;
}

var masks = array[ipmap(128,0,0,0), ipmap(196,0,0,0), ..., ipmap(255,255,255,255)]

function lowrange(w, x, y, z, rangelength) {
  return ipmap(w, x, y, z) & masks[rangelength]
}

function hirange(w, x, y, z, rangelength) {
  return lowrange(w, x, y, z, ,rangelength) + ipmap(255,255,255,255) - masks[rangelength];
}

That ought to do it.

To find whether a particular ip falls in any of the ranges, convert it to an integer and do:

SELECT COUNT(*) FROM ipranges WHERE lowrange <= 1234567 AND 1234567 <= highrange

The query optimizer should be able to speed this up greatly.

Allain Lalonde
Thanks for your reply, how can I convert a subnet into a range using JavaScript? Also what about:123.123.48.0/22123.123.48.0/24They overlap.
Steve
The start and end ips of the range will differ. so the overlap shouldn't be a problem.
Allain Lalonde
Steve
And the ends differ, so what's the problem?
Allain Lalonde
Wouldn't a better solution remove the redundant /24? I guess I could always just take the lowest /x and remove all the higher subnets.
Steve
sure, but that's not part of your question. The fact that they overlap is irrelevant, it'll just be slower.
Allain Lalonde
+4  A: 

The best approach is IMO making use of bitwise operators. For example, 123.123.48.0/22 represents (123<<24)+(123<<16)+(48<<8)+0 (=2071670784; this might be a negative number) as a 32 bit numeric IP address, and -1<<(32-22) = -1024 as a mask. With this, and likewise, your test IP address converted to a number, you can do:

(inputIP & testMask) == testIP

For example, 123.123.49.123 is in that range, as 2071671163 & -1024 is 2071670784

So, here are some tool functions:

function IPnumber(IPaddress) {
    var ip = IPaddress.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/);
    if(ip) {
        return (+ip[1]<<24) + (+ip[2]<<16) + (+ip[3]<<8) + (+ip[4]);
    }
    // else ... ?
    return null;
}

function IPmask(maskSize) {
    return -1<<(32-maskSize)
}

test:

(IPnumber('123.123.49.123') & IPmask('22')) == IPnumber('123.123.48.0')

yields true.

In case your mask is in the format '255.255.252.0', then you can use the IPnumber function for the mask, too.

bart
I like your use of the bitwise operators, but does JavaScript guarantee your indian encoding? I broke out the ip range into low and high to allow for O(logN) behaviour when the columns are indexed as opposed to O(N) using this approach. But it may depend on sqlite's implementation. Not sure.
Allain Lalonde
bart