mlocati/ip-lib

`rangeFromBoundaries` does not work with large ranges

chescos opened this issue · 11 comments

Here's an example:

$range = \IPLib\Factory::rangeFromBoundaries('1.1.1.1', '2.2.2.2');
$cidr = (string) $range; // Results in 0.0.0.0/6

The CIDR result is 0.0.0.0/6 in this case, while the correct output would be a list of CIDRs:

1.1.1.1/32
1.1.1.2/31
1.1.1.4/30
1.1.1.8/29
1.1.1.16/28
1.1.1.32/27
1.1.1.64/26
1.1.1.128/25
1.1.2.0/23
1.1.4.0/22
1.1.8.0/21
1.1.16.0/20
1.1.32.0/19
1.1.64.0/18
1.1.128.0/17
1.2.0.0/15
1.4.0.0/14
1.8.0.0/13
1.16.0.0/12
1.32.0.0/11
1.64.0.0/10
1.128.0.0/9
2.0.0.0/15
2.2.0.0/23
2.2.2.0/31
2.2.2.2/32

Can the rangeFromBoundaries method only be used for ranges that can be displayed in a single CIDR?

rangeFromBoundaries is meant to return a single range containing the two addresses provided.

Oh, I see. Thanks for the super quick response! Wouldn't it be better if the method would throw an error if the two provided addresses can't be displayed in a single CIDR? If I'm not mistaken, the result that the method returns in my example is wrong.

Also, would it be possible to add a method that can return an array of CIDRs when the provided range can't be displayed by a single CIDR? Or is this out of the scope of this library?

I wouldn't change the behavior (and the meaning) of existing methods; that would be a breaking change.
I'd instead add a new method that returns a list of ranges (maybe rangesFromBoundaries?)
Are you willing to implement it?

That sounds very reasonable, yes. I would be willing to implement it but unfortunately, I don't know very much about the different IP notations. So this would be out of the scope of my expertise... but if I can find an implementation of this in a different library (could maybe also be a different language), I might be able to pull it off with some research.

I found a function that seems to work for IPv4, but not for IPv6. I'll see if I can find something that works with IPv6 as well.

function rangesFromBoundaries(string $from, string $to) : array
{
    $start = ip2long($from);
    $end = ip2long($to);
    $result = [];

    while ($end >= $start) {
        $maxSize = 32;

        while ($maxSize > 0) {
            $mask = hexdec(base_convert(
                (pow(2, 32) - pow(2, (32 - ($maxSize - 1)))),
                10, 16
            ));

            $maskBase = $start & $mask;

            if ($maskBase != $start) {
                break;
            }

            $maxSize--;
        }

        $x = log($end - $start + 1) / log(2);
        $maxDiff = floor(32 - floor($x));

        if ($maxSize < $maxDiff){
            $maxSize = $maxDiff;
        }

        $ip = long2ip($start);
        array_push($result, $ip.'/'.$maxSize);
        $start += pow(2, (32 - $maxSize));
    }

    return $result;
}

@chescos where did you find the algorithm above?

I found it in a blog post from Ip2Location on this topic.

I think I found a solution. Tomorrow I should implement it.

See #58

Thanks for all your awesome work!

I just published a new version (1.14.0) with this new rangesFromBoundaries method 😉