Ukuser32
Ukuser32

Reputation: 2189

Get all IP addresses in a range in PHP

I have 900+ IP address ranges from the Microsoft Azure datacentres (http://www.microsoft.com/en-us/download/confirmation.aspx?id=41653) and I want to find out if an IP address is coming from one of these addresses. I've converted it into a single list which could be stored in a text file.

So: is it more costly to loop through each address and use ip2long() such as point 3 here (https://mebsd.com/coding-snipits/php-ipcalc-coding-subnets-ip-addresses.html) OR is it better to convert all IP addresses in the range, store them in a database and then look them up? If the latter, how does one achieve this (i.e. convert a range to all IP addresses within it)?

The key for me is that it must be fast as we'll be checking this fairly often.

Example IP addresses/ranges:

40.112.124.0/24
65.52.128.0/19
94.245.97.0/24
104.47.169.0/24
104.214.240.0/24
137.116.192.0/19
157.55.8.96/27
157.55.8.128/27
157.55.8.160/28
168.63.0.0/19

Upvotes: 2

Views: 3374

Answers (1)

CodeBoy
CodeBoy

Reputation: 3300

Wow, there's almost 1000 IP ranges in the list! To optimize verifying a single IP address is in one of the IP ranges, you definitely should do some pre-processing of the IP range list. Creating a database is one way. I'd look at SQLite as it is a super-fast local file format that can be faster for read-only tasks like this than sending a query to another process (or machine) running a database.

Create a table with columns "range_start" and "range_end" and indexes on these columns. For each IP address range, insert one row with the start and end of the range. You'll have less than 1000 rows when done. Now, to test an IP address ...

SELECT * FROM IP_RANGES WHERE $testIP>=range_start AND $testIP<=range_end

(modify this SQL statement to match how you're calling the database, preferably with PDO, and preferably in an SQL-injection safe way).

UPDATE: Sorry, missed the comments above while I was typing my answer.

@Ukuser32, yes, use ip2long to convert dotted decimal to long ints (much easier to work with; store long ints in the database). To get the end of the range, look at the 2nd part of the CIDR. It is the number (N) of bits, starting from the left, that are fixed within the range. That means 32-N is the number of variable bits. Which means there are 2**(32-N) ips in the range. Which means $range_end can be calculated ...

list($range_start, $range_end) = cidr2range('40.112.124.0/24');
echo "Start: $range_start, end: $range_end\n";

function cidr2range($cidr) {
    $cidr = explode('/', $cidr);
    $range_start = ip2long($cidr[0]);
    $range_end = $range_start + pow(2, 32-intval($cidr[1])) - 1;
    return [$range_start, $range_end];
}

Upvotes: 5

Related Questions