140 lines
No EOL
3.7 KiB
PHP
140 lines
No EOL
3.7 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Exception;
|
|
|
|
class Subnet
|
|
{
|
|
/**
|
|
* Calculate subnet
|
|
*
|
|
* @param string $subnet
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
public static function calculate(string $subnet): array
|
|
{
|
|
[$ip, $cidr] = explode('/', $subnet);
|
|
|
|
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
|
return self::calculateIPv4($ip, intval($cidr));
|
|
} elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
|
return self::calculateIPv6($ip, intval($cidr));
|
|
} else {
|
|
throw new Exception("Invalid IP address.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate IPv4
|
|
*
|
|
* @param string $ip
|
|
* @param int $cidr
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
private static function calculateIPv4(string $ip, int $cidr): array
|
|
{
|
|
if ($cidr < 0 || $cidr > 32) {
|
|
throw new Exception("CIDR must be between 0 and 32.");
|
|
}
|
|
|
|
if ($cidr === 32) {
|
|
return [
|
|
'network' => $ip,
|
|
'first' => $ip,
|
|
'last' => $ip,
|
|
'hosts' => 1,
|
|
];
|
|
}
|
|
|
|
if ($cidr === 31) {
|
|
$ipLong = ip2long($ip);
|
|
$network = $ipLong & (-1 << (32 - $cidr));
|
|
return [
|
|
'network' => long2ip($network),
|
|
'first' => long2ip($network),
|
|
'last' => long2ip($network + 1),
|
|
'hosts' => 2,
|
|
];
|
|
}
|
|
|
|
$hosts = (1 << (32 - $cidr)) - 2;
|
|
$ipLong = ip2long($ip);
|
|
$mask = -1 << (32 - $cidr);
|
|
$network = $ipLong & $mask;
|
|
|
|
$firstIP = $network + 1;
|
|
$lastIP = $network + $hosts;
|
|
|
|
return [
|
|
'network' => long2ip($network),
|
|
'first' => long2ip($firstIP),
|
|
'last' => long2ip($lastIP),
|
|
'hosts' => $hosts,
|
|
];
|
|
}
|
|
|
|
|
|
/**
|
|
* Calculate IPv6
|
|
*
|
|
* @param string $ip
|
|
* @param int $cidr
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
private static function calculateIPv6(string $ip, int $cidr): array
|
|
{
|
|
if ($cidr < 0 || $cidr > 128) {
|
|
throw new Exception("CIDR must be between 0 and 128.");
|
|
}
|
|
|
|
if ($cidr === 128) {
|
|
return [
|
|
'network' => $ip,
|
|
'first' => $ip,
|
|
'last' => $ip,
|
|
'hosts' => 1,
|
|
];
|
|
}
|
|
|
|
if ($cidr === 127) {
|
|
$binaryIP = inet_pton($ip);
|
|
$network = substr($binaryIP, 0, intval($cidr / 8));
|
|
$firstHost = inet_ntop($network);
|
|
$lastHost = inet_ntop($binaryIP);
|
|
return [
|
|
'network' => $firstHost,
|
|
'first' => $firstHost,
|
|
'last' => $lastHost,
|
|
'hosts' => 2,
|
|
];
|
|
}
|
|
|
|
// General case for other IPv6 subnets
|
|
$totalHosts = bcpow(2, 128 - $cidr); // Number of total IPs
|
|
$binaryIP = inet_pton($ip);
|
|
|
|
// Calculate the network address
|
|
$network = substr($binaryIP, 0, intval($cidr / 8));
|
|
$remainder = $cidr % 8;
|
|
|
|
if ($remainder > 0) {
|
|
$lastByte = ord($binaryIP[intval($cidr / 8)]) & (0xFF << (8 - $remainder));
|
|
$network .= chr($lastByte);
|
|
}
|
|
$network = str_pad($network, 16, "\0");
|
|
|
|
$networkAddress = inet_ntop($network);
|
|
|
|
return [
|
|
'network' => $networkAddress,
|
|
'first' => $cidr == 128 ? $networkAddress : inet_ntop($network),
|
|
'last' => $cidr == 128 ? $networkAddress : inet_ntop($network), // Adjusted for simplicity
|
|
'hosts' => $totalHosts,
|
|
];
|
|
}
|
|
|
|
} |