`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;
}
I found it in a blog post from Ip2Location on this topic.
I think I found a solution. Tomorrow I should implement it.
Thanks for all your awesome work!
I just published a new version (1.14.0) with this new rangesFromBoundaries
method 😉