Search code examples
phpipcidr

Check if an IP exists in multiple CIDR ranges


I am building a basic WAF and I have access to multiple CIDR ranges of bad visitors.

My implementation looks like this:

$badIPRanges = [
    '1.2.220.142/31',
    '1.2.220.144/29',
    '1.2.220.152/30',
];

function isBlackListed(string $ip, array $ipRanges): bool {
    foreach ($ipRanges as $ipRange) {
        if (ipInRange($ip, $ipRange)) {
            return true;
        }
    }
    return false;
}

function ipInRange(string $ip, string $ipRange): bool {
    [$net, $mask] = explode('/', $ipRange);
    $ip_net = ip2long($net);
    $ip_mask = ~((1 << (32 - $mask)) - 1);
    $ip_ip = ip2long($ip);
    $ip_ip_net = $ip_ip & $ip_mask;
    return ($ip_ip_net == $ip_net);
}

var_dump(isBlackListed('1.2.220.145', $badIPRanges) ? 'Yes' : 'No');

I expected 1.2.220.145 not to be in the multiple CIDR ranges, but apparently not. I am getting Yes back from the above test code.

Am I doing something wrong? or is this valid? The original implementation was taken from here.

Using https://tehnoblog.org/ip-tools/ip-address-in-cidr-range/ I verified that 1.2.220.145 is in the 1.2.220.144/29 CIDR block, so does this mean the CIDR block 1.2.220.142/31 is redundant in my $badIPRanges list?


Solution

  • Unless you've deeply internalized the format, you can seldom look at the dotted quad notation and have any idea what the subnetting is for anything other than a multiple of 8.

    The binary representation of those networks is:

    1.2.220.142 31 00000001 00000010 11011100 10001110
    1.2.220.144 29 00000001 00000010 11011100 10010000
    1.2.220.152 30 00000001 00000010 11011100 10011000
    

    1.2.220.142/31 is wholly different from the other two, and none overlap.